Browse Source

删除冗余文件 添加帖子评估

刘立冬 2 weeks ago
parent
commit
047f929779
70 changed files with 49791 additions and 33149 deletions
  1. 0 117
      RUN_BATCH_README.md
  2. 299 0
      evaluate_existing_results.py
  3. 115 53
      knowledge_search_traverse.py
  4. 294 0
      post_evaluator.py
  5. 0 193
      sug_v1.py
  6. 0 316
      sug_v1_1.py
  7. 0 179
      sug_v1_2.py
  8. 0 312
      sug_v2.py
  9. 0 311
      sug_v2_1.py
  10. 0 307
      sug_v2_1_0.py
  11. 0 301
      sug_v2_raw_v1.py
  12. 0 291
      sug_v2_raw_v2.py
  13. 0 383
      sug_v2_with_eval_requery_yx.py
  14. 0 349
      sug_v2_with_eval_yx.py
  15. 0 650
      sug_v2_with_eval_yx_v2.py
  16. 0 312
      sug_v2_with_requery_yx.py
  17. 0 378
      sug_v3.py
  18. 0 507
      sug_v4.py
  19. 0 507
      sug_v4_1.py
  20. 0 468
      sug_v4_2.py
  21. 0 507
      sug_v4_with_yx.py
  22. 0 726
      sug_v5.py
  23. 0 728
      sug_v5_0.py
  24. 0 965
      sug_v5_0_with_eval_v2_yx.py
  25. 0 1016
      sug_v5_0_with_eval_v2_zou_yx.py
  26. 0 765
      sug_v5_1.py
  27. 0 726
      sug_v5_2.py
  28. 0 728
      sug_v5_3.py
  29. 0 632
      sug_v6_0_progressive_exploration.py
  30. 0 824
      sug_v6_1_2.py
  31. 0 656
      sug_v6_1_2_0.py
  32. 0 811
      sug_v6_1_2_1.py
  33. 1061 0
      sug_v6_1_2_10.py
  34. 1103 0
      sug_v6_1_2_114.py
  35. 1 1
      sug_v6_1_2_117.py
  36. 44 0
      sug_v6_1_2_118.py
  37. 1690 0
      sug_v6_1_2_120.py
  38. 3 4
      sug_v6_1_2_122.py
  39. 2756 0
      sug_v6_1_2_124.py
  40. 3894 0
      sug_v6_1_2_126.py
  41. 3941 0
      sug_v6_1_2_127.py
  42. 3880 0
      sug_v6_1_2_128.py
  43. 0 811
      sug_v6_1_2_2.py
  44. 0 1908
      sug_v6_1_2_3.py
  45. 0 2180
      sug_v6_1_2_3.visualize.py
  46. 0 1908
      sug_v6_1_2_4.py
  47. 0 1551
      sug_v6_1_2_5.py
  48. 0 1551
      sug_v6_1_2_6.py
  49. 0 1213
      sug_v6_1_2_7.py
  50. 990 0
      sug_v6_1_2_8_流程分析.md
  51. 0 651
      sug_v6_1_intent_relevance.py
  52. 0 557
      sug_v6_2_combinatorial.py
  53. 0 820
      sug_v6_3_with_annotation.py
  54. 0 966
      sug_v6_4_with_annotation.py
  55. 0 984
      sug_v6_5_with_annotation.py
  56. 0 415
      test.py
  57. 0 129
      test_cache.py
  58. 183 68
      visualization.html
  59. 0 321
      visualization/knowledge_search_traverse/convert_v8_to_graph.js
  60. 18 6
      visualization/knowledge_search_traverse/convert_v8_to_graph_v3.js
  61. 28857 0
      visualization/knowledge_search_traverse/debug_component.jsx
  62. BIN
      visualization/knowledge_search_traverse/image.png
  63. 145 4
      visualization/knowledge_search_traverse/index.js
  64. 7 0
      visualization/knowledge_search_traverse/test_component.jsx
  65. 215 0
      visualization/sug_v6_1_2_121/README.md
  66. 76 4
      visualization/sug_v6_1_2_121/convert_v8_to_graph_v3.js
  67. 215 3
      visualization/sug_v6_1_2_121/index.js
  68. 4 3
      visualization/sug_v6_1_2_121/package.json
  69. 0 107
      visualization/sug_v6_1_2_5/README.md
  70. 0 1966
      visualization/sug_v6_1_2_5/index.js

+ 0 - 117
RUN_BATCH_README.md

@@ -1,117 +0,0 @@
-# 批量执行脚本使用说明
-
-## 功能
-`run_batch.sh` 可以批量执行多个Python脚本,对指定父目录下的所有子目录逐一处理。
-
-## 用法
-
-```bash
-./run_batch.sh <父目录> <脚本1.py> [脚本2.py] [脚本3.py] ...
-```
-
-## 参数说明
-
-- **父目录**: 包含多个输入子目录的父目录
-- **脚本列表**: 一个或多个要执行的Python脚本
-
-## 执行逻辑
-
-1. 获取父目录下的所有子目录
-2. 对每个子目录,依次执行所有指定的Python脚本
-3. 将子目录路径作为 `--input-dir` 参数传递给脚本
-4. 统计执行结果(成功/失败)
-
-## 使用示例
-
-### 示例1: 单个脚本处理所有子目录
-
-```bash
-./run_batch.sh input sug_v2.py
-```
-
-假设目录结构:
-```
-input/
-├── 简单扣图/
-├── 复杂任务/
-└── 其他任务/
-```
-
-执行结果:
-- `python sug_v2.py --input-dir input/简单扣图`
-- `python sug_v2.py --input-dir input/复杂任务`
-- `python sug_v2.py --input-dir input/其他任务`
-
-### 示例2: 多个脚本处理所有子目录
-
-```bash
-./run_batch.sh input sug_v2.py sug_v3.py
-```
-
-执行结果:
-- `python sug_v2.py --input-dir input/简单扣图`
-- `python sug_v3.py --input-dir input/简单扣图`
-- `python sug_v2.py --input-dir input/复杂任务`
-- `python sug_v3.py --input-dir input/复杂任务`
-- `python sug_v2.py --input-dir input/其他任务`
-- `python sug_v3.py --input-dir input/其他任务`
-
-## 输出示例
-
-```
-=========================================
-开始批量执行
-父目录: input
-脚本列表: sug_v2.py sug_v3.py
-=========================================
-
-----------------------------------------
-处理子目录: input/简单扣图
-----------------------------------------
-
-[任务 1] 执行: python sug_v2.py --input-dir input/简单扣图
-开始时间: 2025-10-24 15:30:00
-... (脚本输出) ...
-✓ 成功
-结束时间: 2025-10-24 15:30:45
-
-[任务 2] 执行: python sug_v3.py --input-dir input/简单扣图
-开始时间: 2025-10-24 15:30:45
-... (脚本输出) ...
-✓ 成功
-结束时间: 2025-10-24 15:31:30
-
-----------------------------------------
-处理子目录: input/复杂任务
-----------------------------------------
-
-... (继续执行) ...
-
-=========================================
-批量执行完成
-=========================================
-总任务数: 6
-成功: 5
-失败: 1
-=========================================
-```
-
-## 特性
-
-- ✅ 自动发现父目录下的所有子目录
-- ✅ 支持执行多个Python脚本
-- ✅ 显示每个任务的执行时间
-- ✅ 统计成功/失败数量
-- ✅ 即使某个任务失败也会继续执行
-- ✅ 最终返回执行结果(有失败则退出码非零)
-
-## 前置要求
-
-- 每个子目录必须包含 `context.md` 和 `q.md` 文件
-- Python脚本必须支持 `--input-dir` 参数
-
-## 注意事项
-
-1. 脚本会按照子目录的字母顺序执行
-2. 如果某个任务失败,会继续执行剩余任务
-3. 最终如果有任何失败,脚本会返回非零退出码

+ 299 - 0
evaluate_existing_results.py

@@ -0,0 +1,299 @@
+#!/usr/bin/env python3
+"""
+离线评估脚本 - 对已有的run_context.json中的帖子进行评估
+
+功能:
+1. 读取run_context.json
+2. 提取所有搜索结果中的帖子
+3. 批量评估(跳过已评估的)
+4. 更新run_context.json
+5. 生成评估报告
+"""
+
+import asyncio
+import json
+import argparse
+from pathlib import Path
+from datetime import datetime
+from typing import Dict, List
+from pydantic import BaseModel, Field
+
+from post_evaluator import evaluate_all_posts, apply_evaluation_to_post, get_relevance_level
+
+
+# ============================================================================
+# 数据模型(与knowledge_search_traverse.py保持一致)
+# ============================================================================
+
+class Post(BaseModel):
+    """帖子(需要与主程序的Post模型一致)"""
+    title: str = ""
+    body_text: str = ""
+    type: str = "normal"  # video/normal
+    images: list[str] = Field(default_factory=list)
+    video: str = ""
+    interact_info: dict = Field(default_factory=dict)
+    note_id: str = ""
+    note_url: str = ""
+
+    # 评估字段
+    is_knowledge: bool | None = None
+    knowledge_reason: str = ""
+    relevance_score: float | None = None
+    relevance_level: str = ""
+    relevance_reason: str = ""
+    evaluation_time: str = ""
+
+
+class Search(BaseModel):
+    """搜索结果"""
+    text: str
+    score_with_o: float = 0.0
+    reason: str = ""
+    from_q: dict | None = None
+    post_list: list[Post] = Field(default_factory=list)
+
+
+# ============================================================================
+# 辅助函数
+# ============================================================================
+
+def load_run_context(file_path: str) -> dict:
+    """
+    加载run_context.json
+
+    Args:
+        file_path: run_context.json文件路径
+
+    Returns:
+        run_context字典
+    """
+    with open(file_path, 'r', encoding='utf-8') as f:
+        return json.load(f)
+
+
+def save_run_context(file_path: str, run_context: dict):
+    """
+    保存run_context.json
+
+    Args:
+        file_path: run_context.json文件路径
+        run_context: run_context字典
+    """
+    with open(file_path, 'w', encoding='utf-8') as f:
+        json.dump(run_context, f, ensure_ascii=False, indent=2)
+    print(f"\n✅ 已保存到: {file_path}")
+
+
+def extract_all_posts(run_context: dict) -> List[Post]:
+    """
+    从run_context中提取所有帖子
+
+    Args:
+        run_context: run_context字典
+
+    Returns:
+        所有帖子的列表(Post对象)
+    """
+    all_posts = []
+    post_id_set = set()  # 用于去重
+
+    # 遍历所有轮次
+    for round_data in run_context.get("rounds", []):
+        # 获取搜索结果
+        search_results = round_data.get("search_results", [])
+
+        for search_data in search_results:
+            for post_data in search_data.get("post_list", []):
+                note_id = post_data.get("note_id", "")
+
+                # 去重
+                if note_id and note_id not in post_id_set:
+                    post_id_set.add(note_id)
+                    post = Post(**post_data)
+                    all_posts.append(post)
+
+    return all_posts
+
+
+def update_posts_in_context(run_context: dict, evaluated_posts: Dict[str, Post]):
+    """
+    更新run_context中的帖子数据
+
+    Args:
+        run_context: run_context字典
+        evaluated_posts: 已评估的帖子字典 {note_id: Post}
+    """
+    updated_count = 0
+
+    # 遍历所有轮次
+    for round_data in run_context.get("rounds", []):
+        # 获取搜索结果
+        search_results = round_data.get("search_results", [])
+
+        for search_data in search_results:
+            for i, post_data in enumerate(search_data.get("post_list", [])):
+                note_id = post_data.get("note_id", "")
+
+                if note_id in evaluated_posts:
+                    # 更新帖子数据
+                    evaluated_post = evaluated_posts[note_id]
+                    search_data["post_list"][i] = evaluated_post.dict()
+                    updated_count += 1
+
+    print(f"✅ 更新了 {updated_count} 个帖子的评估数据")
+
+
+def generate_evaluation_report(posts: List[Post]):
+    """
+    生成评估报告
+
+    Args:
+        posts: 帖子列表
+    """
+    print("\n" + "=" * 60)
+    print("📊 评估报告")
+    print("=" * 60)
+
+    total = len(posts)
+    evaluated = sum(1 for p in posts if p.is_knowledge is not None)
+    unevaluated = total - evaluated
+
+    print(f"\n总帖子数: {total}")
+    print(f"已评估: {evaluated}")
+    print(f"未评估: {unevaluated}")
+
+    if evaluated == 0:
+        print("\n⚠️  没有评估数据")
+        return
+
+    # 知识判定统计
+    knowledge_count = sum(1 for p in posts if p.is_knowledge is True)
+    non_knowledge_count = sum(1 for p in posts if p.is_knowledge is False)
+
+    print(f"\n--- 知识判定 ---")
+    print(f"知识内容: {knowledge_count} ({knowledge_count/evaluated*100:.1f}%)")
+    print(f"非知识内容: {non_knowledge_count} ({non_knowledge_count/evaluated*100:.1f}%)")
+
+    # 相关性统计
+    scores = [p.relevance_score for p in posts if p.relevance_score is not None]
+    if scores:
+        avg_score = sum(scores) / len(scores)
+        max_score = max(scores)
+        min_score = min(scores)
+
+        high_count = sum(1 for p in posts if p.relevance_level == "高度相关")
+        mid_count = sum(1 for p in posts if p.relevance_level == "中度相关")
+        low_count = sum(1 for p in posts if p.relevance_level == "低度相关")
+
+        print(f"\n--- 相关性评估 ---")
+        print(f"平均得分: {avg_score:.2f}")
+        print(f"最高得分: {max_score:.2f}")
+        print(f"最低得分: {min_score:.2f}")
+        print(f"\n高度相关: {high_count} ({high_count/evaluated*100:.1f}%)")
+        print(f"中度相关: {mid_count} ({mid_count/evaluated*100:.1f}%)")
+        print(f"低度相关: {low_count} ({low_count/evaluated*100:.1f}%)")
+
+    # 高质量帖子(知识+高相关性)
+    high_quality = sum(1 for p in posts
+                      if p.is_knowledge is True
+                      and p.relevance_level == "高度相关")
+
+    print(f"\n--- 高质量帖子 ---")
+    print(f"知识内容 + 高度相关: {high_quality} ({high_quality/evaluated*100:.1f}%)")
+
+    print("\n" + "=" * 60)
+
+
+# ============================================================================
+# 主函数
+# ============================================================================
+
+async def main():
+    parser = argparse.ArgumentParser(description='对已有run_context.json中的帖子进行离线评估')
+    parser.add_argument(
+        '--input',
+        type=str,
+        required=True,
+        help='run_context.json文件路径'
+    )
+    parser.add_argument(
+        '--query',
+        type=str,
+        default=None,
+        help='原始问题(如果json中没有则必须指定)'
+    )
+    parser.add_argument(
+        '--force',
+        action='store_true',
+        help='强制重新评估所有帖子(包括已评估的)'
+    )
+    parser.add_argument(
+        '--report-only',
+        action='store_true',
+        help='仅生成评估报告,不执行新评估'
+    )
+
+    args = parser.parse_args()
+
+    # 加载run_context
+    print(f"📂 加载文件: {args.input}")
+    run_context = load_run_context(args.input)
+
+    # 获取原始问题
+    original_query = args.query or run_context.get("o", "")
+    if not original_query:
+        print("❌ 错误: 无法获取原始问题,请使用 --query 参数指定")
+        return
+
+    print(f"❓ 原始问题: {original_query}")
+
+    # 提取所有帖子
+    all_posts = extract_all_posts(run_context)
+    print(f"📋 提取到 {len(all_posts)} 个帖子")
+
+    if len(all_posts) == 0:
+        print("⚠️  没有找到帖子数据")
+        return
+
+    # 仅生成报告模式
+    if args.report_only:
+        generate_evaluation_report(all_posts)
+        return
+
+    # 筛选需要评估的帖子
+    if args.force:
+        posts_to_evaluate = all_posts
+        print(f"🔄 强制重新评估所有帖子")
+    else:
+        posts_to_evaluate = [p for p in all_posts if p.is_knowledge is None]
+        print(f"🆕 发现 {len(posts_to_evaluate)} 个未评估的帖子")
+
+    if len(posts_to_evaluate) == 0:
+        print("✅ 所有帖子已评估,使用 --force 强制重新评估")
+        generate_evaluation_report(all_posts)
+        return
+
+    # 批量评估
+    print(f"\n🚀 开始评估...")
+    evaluation_results = await evaluate_all_posts(posts_to_evaluate, original_query)
+
+    # 应用评估结果到Post对象
+    evaluated_posts = {}
+    for post in posts_to_evaluate:
+        if post.note_id in evaluation_results:
+            apply_evaluation_to_post(post, evaluation_results[post.note_id])
+            evaluated_posts[post.note_id] = post
+
+    # 更新run_context
+    update_posts_in_context(run_context, evaluated_posts)
+
+    # 保存
+    save_run_context(args.input, run_context)
+
+    # 生成报告(使用更新后的all_posts)
+    generate_evaluation_report(all_posts)
+
+
+if __name__ == "__main__":
+    asyncio.run(main())

+ 115 - 53
knowledge_search_traverse.py

@@ -17,7 +17,8 @@ MODEL_NAME = "google/gemini-2.5-flash"
 REQUIRED_SCORE_GAIN = 0.02
 from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
 from script.search.xiaohongshu_search import XiaohongshuSearch
-from multimodal_extractor import extract_post_images
+# from multimodal_extractor import extract_post_images  # 内容提取流程已断开
+from post_evaluator import evaluate_post, apply_evaluation_to_post
 
 
 # ============================================================================
@@ -145,6 +146,14 @@ class Post(BaseModel):
     note_id: str = ""
     note_url: str = ""
 
+    # 评估字段
+    is_knowledge: bool | None = None  # 是否是知识内容
+    knowledge_reason: str = ""  # 知识判定原因
+    relevance_score: float | None = None  # 与原始query的相关性(0-1)
+    relevance_level: str = ""  # 相关性分级:"高度相关" | "中度相关" | "低度相关"
+    relevance_reason: str = ""  # 相关性评分原因
+    evaluation_time: str = ""  # 评估时间戳
+
 
 class Search(Sug):
     """搜索结果(继承Sug)"""
@@ -1994,23 +2003,38 @@ def process_note_data(note: dict) -> Post:
 
     # ========== 调试日志 START ==========
     note_id = note.get("id", "")
-    raw_title = note_card.get("display_title")  # 不提供默认值
-    raw_body = note_card.get("desc")
-    raw_type = note_card.get("type")
-
-    # 打印原始值类型和内容
-    print(f"\n[DEBUG] 处理帖子 {note_id}:")
-    print(f"  raw_title 类型: {type(raw_title).__name__}, 值: {repr(raw_title)}")
-    print(f"  raw_body 类型: {type(raw_body).__name__}, 值: {repr(raw_body)[:100] if raw_body else repr(raw_body)}")
-    print(f"  raw_type 类型: {type(raw_type).__name__}, 值: {repr(raw_type)}")
-
-    # 检查是否为 None
-    if raw_title is None:
-        print(f"  ⚠️  WARNING: display_title 是 None!")
-    if raw_body is None:
-        print(f"  ⚠️  WARNING: desc 是 None!")
-    if raw_type is None:
-        print(f"  ⚠️  WARNING: type 是 None!")
+
+    # 1. 打印完整的 note 结构
+    print(f"\n[DEBUG] ===== 处理帖子 {note_id} =====")
+    print(f"[DEBUG] note 的所有键: {list(note.keys())}")
+    print(f"[DEBUG] note 完整数据 (前2000字符):")
+    print(json.dumps(note, ensure_ascii=False, indent=2)[:2000])
+
+    # 2. 打印 note_card 信息
+    print(f"\n[DEBUG] note_card 的所有键: {list(note_card.keys())}")
+
+    # 3. 检查 desc 字段
+    raw_desc = note_card.get("desc")
+    print(f"\n[DEBUG] desc 字段:")
+    print(f"  - 类型: {type(raw_desc).__name__}")
+    print(f"  - 长度: {len(raw_desc) if raw_desc else 0}")
+    print(f"  - 完整内容: {repr(raw_desc)}")
+
+    # 4. 检查是否有其他可能包含完整内容的字段
+    print(f"\n[DEBUG] 检查其他可能的内容字段:")
+    for potential_field in ["full_desc", "content", "full_content", "note_text", "body", "full_body", "title", "display_title"]:
+        if potential_field in note_card:
+            value = note_card.get(potential_field)
+            print(f"  - 发现字段 '{potential_field}': 长度={len(str(value))}, 值={repr(str(value)[:200])}")
+
+    # 5. 检查顶层 note 对象中是否有详细内容
+    print(f"\n[DEBUG] 检查 note 顶层字段:")
+    for top_field in ["note_info", "detail", "content", "desc"]:
+        if top_field in note:
+            value = note.get(top_field)
+            print(f"  - 发现顶层字段 '{top_field}': 类型={type(value).__name__}, 内容={repr(str(value)[:200])}")
+
+    print(f"[DEBUG] ===== 数据检查完成 =====\n")
     # ========== 调试日志 END ==========
 
     # 提取图片URL - 使用新的字段名 image_url
@@ -2031,7 +2055,8 @@ def process_note_data(note: dict) -> Post:
             # 尝试获取视频URL
             video_url = video_info.get("media", {}).get("stream", {}).get("h264", [{}])[0].get("master_url", "")
 
-    return Post(
+    # 构造 Post 对象
+    post = Post(
         note_id=note.get("id") or "",
         title=note_card.get("display_title") or "",
         body_text=note_card.get("desc") or "",
@@ -2047,6 +2072,19 @@ def process_note_data(note: dict) -> Post:
         note_url=f"https://www.xiaohongshu.com/explore/{note.get('id', '')}"
     )
 
+    # 打印最终构造的 Post 对象
+    print(f"\n[DEBUG] ===== 构造的 Post 对象 =====")
+    print(f"[DEBUG] - note_id: {post.note_id}")
+    print(f"[DEBUG] - title: {post.title}")
+    print(f"[DEBUG] - body_text 长度: {len(post.body_text)}")
+    print(f"[DEBUG] - body_text 完整内容: {repr(post.body_text)}")
+    print(f"[DEBUG] - type: {post.type}")
+    print(f"[DEBUG] - images 数量: {len(post.images)}")
+    print(f"[DEBUG] - interact_info: {post.interact_info}")
+    print(f"[DEBUG] ===== Post 对象构造完成 =====\n")
+
+    return post
+
 
 async def evaluate_with_o(text: str, o: str, cache: dict[str, tuple[float, str]] | None = None) -> tuple[float, str]:
     """评估文本与原始问题o的相关度
@@ -2827,6 +2865,17 @@ async def run_round(
 
         search_tasks = [search_for_sug(sug) for sug in high_score_sugs]
         search_list = await asyncio.gather(*search_tasks)
+
+        # 评估搜索结果中的帖子
+        print(f"\n[评估] 评估搜索结果中的帖子...")
+        for search in search_list:
+            if search.post_list:
+                print(f"  评估来自 '{search.text}' 的 {len(search.post_list)} 个帖子")
+                # 对每个帖子进行评估
+                for post in search.post_list:
+                    evaluation = await evaluate_post(post, o, semaphore=None)
+                    if evaluation:
+                        apply_evaluation_to_post(post, evaluation)
     else:
         print(f"  没有高分建议词,search_list为空")
 
@@ -3412,13 +3461,13 @@ async def run_round_v2(
     print(f"  找到 {len(high_score_sugs)} 个高分SUG")
 
     search_list = []
-    extraction_results = {}  # 🆕 收集提取结果
+    # extraction_results = {}  # 内容提取流程已断开
 
     if len(high_score_sugs) > 0:
-        async def search_for_sug(sug: Sug) -> tuple[Search, dict]:
-            """返回Search和提取结果"""
+        async def search_for_sug(sug: Sug) -> Search:
+            """返回Search结果"""
             print(f"    搜索: {sug.text}")
-            post_extractions = {}  # 当前SUG的提取结果
+            # post_extractions = {}  # 内容提取流程已断开
 
             try:
                 search_result = xiaohongshu_search.search(keyword=sug.text)
@@ -3433,11 +3482,11 @@ async def run_round_v2(
                 for note in notes[:10]:
                     post = process_note_data(note)
 
-                    # 🆕 多模态提取(搜索后立即处理)
-                    if post.type == "normal" and len(post.images) > 0:
-                        extraction = await extract_post_images(post)
-                        if extraction:
-                            post_extractions[post.note_id] = extraction
+                    # # 🆕 多模态提取(搜索后立即处理) - 内容提取流程已断开
+                    # if post.type == "normal" and len(post.images) > 0:
+                    #     extraction = await extract_post_images(post)
+                    #     if extraction:
+                    #         post_extractions[post.note_id] = extraction
 
                     post_list.append(post)
 
@@ -3448,7 +3497,8 @@ async def run_round_v2(
                     score_with_o=sug.score_with_o,
                     from_q=sug.from_q,
                     post_list=post_list
-                ), post_extractions
+                )
+                # , post_extractions  # 内容提取流程已断开
 
             except Exception as e:
                 print(f"      ✗ 搜索失败: {e}")
@@ -3457,15 +3507,27 @@ async def run_round_v2(
                     score_with_o=sug.score_with_o,
                     from_q=sug.from_q,
                     post_list=[]
-                ), {}
+                )
+                # , {}  # 内容提取流程已断开
 
         search_tasks = [search_for_sug(sug) for sug in high_score_sugs]
         results = await asyncio.gather(*search_tasks)
 
-        # 分离搜索结果和提取结果
-        for search, extractions in results:
+        # 收集搜索结果
+        for search in results:
             search_list.append(search)
-            extraction_results.update(extractions)
+            # extraction_results.update(extractions)  # 内容提取流程已断开
+
+        # 评估搜索结果中的帖子
+        print(f"\n[评估] 评估搜索结果中的帖子...")
+        for search in search_list:
+            if search.post_list:
+                print(f"  评估来自 '{search.text}' 的 {len(search.post_list)} 个帖子")
+                # 对每个帖子进行评估
+                for post in search.post_list:
+                    evaluation = await evaluate_post(post, o, semaphore=None)
+                    if evaluation:
+                        apply_evaluation_to_post(post, evaluation)
 
     # 步骤4: 生成N域组合
     print(f"\n[步骤4] 生成{round_num}域组合...")
@@ -3690,10 +3752,10 @@ async def run_round_v2(
     print(f"  高分SUG数: {len(high_score_sugs)}")
     print(f"  高增益SUG: {len(high_gain_sugs)}")
     print(f"  搜索数: {len(search_list)}")
-    print(f"  提取帖子数: {len(extraction_results)}")  # 🆕
+    # print(f"  提取帖子数: {len(extraction_results)}")  # 内容提取流程已断开
     print(f"  下轮Query数: {len(q_list_next)}")
 
-    return q_list_next, search_list, extraction_results  # 🆕 返回提取结果
+    return q_list_next, search_list  # 不再返回提取结果
 
 
 async def iterative_loop_v2(
@@ -3718,7 +3780,7 @@ async def iterative_loop_v2(
 
     # 收集所有搜索结果
     all_search_list = []
-    all_extraction_results = {}  # 🆕 收集所有提取结果
+    # all_extraction_results = {}  # 内容提取流程已断开
 
     # 准备 Round 1 的输入:从 segments 提取所有 words
     query_input = extract_words_from_segments(segments)
@@ -3730,7 +3792,7 @@ async def iterative_loop_v2(
     round_num = 1
 
     while query_input and round_num <= actual_max_rounds:
-        query_input, search_list, extraction_results = await run_round_v2(  # 🆕 接收提取结果
+        query_input, search_list = await run_round_v2(  # 不再接收提取结果
             round_num=round_num,
             query_input=query_input,  # 传递上一轮的输出
             segments=segments,
@@ -3742,7 +3804,7 @@ async def iterative_loop_v2(
         )
 
         all_search_list.extend(search_list)
-        all_extraction_results.update(extraction_results)  # 🆕 合并提取结果
+        # all_extraction_results.update(extraction_results)  # 内容提取流程已断开
 
         # 如果没有新的query,提前结束
         if not query_input:
@@ -3756,10 +3818,10 @@ async def iterative_loop_v2(
     print(f"  实际轮数: {round_num}")
     print(f"  总搜索次数: {len(all_search_list)}")
     print(f"  总帖子数: {sum(len(s.post_list) for s in all_search_list)}")
-    print(f"  提取帖子数: {len(all_extraction_results)}")  # 🆕
+    # print(f"  提取帖子数: {len(all_extraction_results)}")  # 内容提取流程已断开
     print(f"{'='*60}")
 
-    return all_search_list, all_extraction_results  # 🆕 返回提取结果
+    return all_search_list  # 不再返回提取结果
 
 
 # ============================================================================
@@ -3814,7 +3876,7 @@ async def main(input_dir: str, max_rounds: int = 2, sug_threshold: float = 0.7,
         print(f"{'='*60}\n")
 
         # 执行迭代 (v121: 使用新架构)
-        all_search_list, all_extraction_results = await iterative_loop_v2(  # 🆕 接收提取结果
+        all_search_list = await iterative_loop_v2(  # 不再接收提取结果
             run_context,
             max_rounds=max_rounds,
             sug_threshold=sug_threshold
@@ -3825,7 +3887,7 @@ async def main(input_dir: str, max_rounds: int = 2, sug_threshold: float = 0.7,
         output += f"原始问题:{run_context.o}\n"
         output += f"总搜索次数:{len(all_search_list)}\n"
         output += f"总帖子数:{sum(len(s.post_list) for s in all_search_list)}\n"
-        output += f"提取帖子数:{len(all_extraction_results)}\n"  # 🆕
+        # output += f"提取帖子数:{len(all_extraction_results)}\n"  # 内容提取流程已断开
         output += "\n" + "="*60 + "\n"
 
         if all_search_list:
@@ -3862,17 +3924,17 @@ async def main(input_dir: str, max_rounds: int = 2, sug_threshold: float = 0.7,
             json.dump(search_results_data, f, ensure_ascii=False, indent=2)
         print(f"Search results saved to: {search_results_path}")
 
-        # 🆕 保存图片提取结果
-        if all_extraction_results:
-            extraction_path = os.path.join(run_context.log_dir, "search_extract.json")
-            extraction_data = {
-                note_id: extraction.model_dump()
-                for note_id, extraction in all_extraction_results.items()
-            }
-            with open(extraction_path, "w", encoding="utf-8") as f:
-                json.dump(extraction_data, f, ensure_ascii=False, indent=2)
-            print(f"Image extractions saved to: {extraction_path}")
-            print(f"  提取了 {len(all_extraction_results)} 个帖子的图片内容")
+        # # 🆕 保存图片提取结果 - 内容提取流程已断开
+        # if all_extraction_results:
+        #     extraction_path = os.path.join(run_context.log_dir, "search_extract.json")
+        #     extraction_data = {
+        #         note_id: extraction.model_dump()
+        #         for note_id, extraction in all_extraction_results.items()
+        #     }
+        #     with open(extraction_path, "w", encoding="utf-8") as f:
+        #         json.dump(extraction_data, f, ensure_ascii=False, indent=2)
+        #     print(f"Image extractions saved to: {extraction_path}")
+        #     print(f"  提取了 {len(all_extraction_results)} 个帖子的图片内容")
 
         # 可视化
         if visualize:

+ 294 - 0
post_evaluator.py

@@ -0,0 +1,294 @@
+"""
+帖子评估模块
+
+功能:
+1. 评估帖子是否包含面向创作的内容知识
+2. 评估帖子与原始query的相关性
+3. 支持多模态评估(文本+图片)
+4. 支持批量并发评估
+"""
+
+import asyncio
+import json
+import os
+from datetime import datetime
+from typing import Optional, Tuple
+from pydantic import BaseModel, Field
+import requests
+
+MODEL_NAME = "google/gemini-2.5-flash"
+MAX_IMAGES_PER_POST = 10  # 最大处理图片数
+MAX_CONCURRENT_EVALUATIONS = 5  # 最大并发评估数
+API_TIMEOUT = 120  # API 超时时间(秒)
+
+
+# ============================================================================
+# 数据模型
+# ============================================================================
+
+class EvaluationResult(BaseModel):
+    """评估结果"""
+    is_knowledge: bool = Field(..., description="是否是知识内容")
+    knowledge_reason: str = Field(..., description="知识判定理由")
+    relevance_score: float = Field(..., description="相关性得分(0-1)")
+    relevance_reason: str = Field(..., description="相关性评分理由")
+
+
+# ============================================================================
+# Prompt 定义
+# ============================================================================
+
+EVALUATION_PROMPT_TEMPLATE = """
+你是一名专业的内容分析专家,请对以下小红书帖子进行两项评估。
+
+## 原始问题
+{original_query}
+
+## 帖子信息
+**标题**: {title}
+**正文**: {body_text}
+**图片数量**: {num_images}张
+
+## 评估任务
+
+### 任务1: 知识判定
+判断这个帖子是否包含"面向创作的内容知识"。
+
+**判断标准:**
+- **是知识内容**: 包含可复用的创作方法、技巧、工具使用、流程步骤、教程指导等实用知识
+- **非知识内容**: 纯个人分享、日常记录、商品推广、无实质性创作指导、纯情感表达等
+
+**要求**:
+- 综合分析标题、正文和图片(如有)
+- 重点关注是否有可学习、可复用的创作知识
+
+### 任务2: 相关性评估
+评估这个帖子与原始问题的相关性。
+
+**评估维度:**
+1. 帖子内容能否解决原始问题?
+2. 是否包含原始问题所需的知识/方法/工具?
+3. 内容的针对性和完整性如何?
+
+**评分标准:**
+- 0.7-1.0: 高度相关 - 直接回答问题,内容针对性强
+- 0.4-0.7: 中度相关 - 部分相关,有一定参考价值
+- 0.0-0.4: 低度相关 - 相关性弱,参考价值有限
+
+## 输出要求
+必须返回一个JSON对象,包含以下字段:
+{{
+  "is_knowledge": true/false,
+  "knowledge_reason": "知识判定理由(100字以内,简明扼要)",
+  "relevance_score": 0.85,
+  "relevance_reason": "相关性分析(150字以内,说明相关程度和原因)"
+}}
+
+## 重要提示
+- 两项评估相互独立:一个帖子可以是知识内容但与问题不相关,也可以相关但不是知识内容
+- 理由要具体,指出关键要素
+- 分数要准确,体现真实的相关程度
+""".strip()
+
+
+# ============================================================================
+# 核心评估函数
+# ============================================================================
+
+async def evaluate_post(
+    post,  # Post对象
+    original_query: str,
+    semaphore: Optional[asyncio.Semaphore] = None
+) -> Optional[EvaluationResult]:
+    """
+    评估单个帖子(知识判定 + 相关性评估)
+
+    Args:
+        post: Post对象(包含title, body_text, images, type等)
+        original_query: 原始问题
+        semaphore: 可选的信号量用于并发控制
+
+    Returns:
+        EvaluationResult对象,评估失败返回None
+    """
+    # 视频帖子跳过
+    if post.type == "video":
+        print(f"      ⊗ 跳过视频帖子: {post.note_id}")
+        return None
+
+    # 准备图片列表(限制数量)
+    image_urls = post.images[:MAX_IMAGES_PER_POST] if post.images else []
+    image_count = len(image_urls)
+
+    print(f"      🔍 开始评估帖子: {post.note_id} ({image_count}张图片)")
+
+    try:
+        # 如果有信号量,使用它进行并发控制
+        if semaphore:
+            async with semaphore:
+                result = await _evaluate_post_internal(post, original_query, image_urls)
+        else:
+            result = await _evaluate_post_internal(post, original_query, image_urls)
+
+        print(f"      ✅ 评估完成: {post.note_id} | 知识:{result.is_knowledge} | 相关性:{result.relevance_score:.2f}")
+        return result
+
+    except Exception as e:
+        print(f"      ❌ 评估失败: {post.note_id} - {str(e)[:100]}")
+        return None
+
+
+async def _evaluate_post_internal(post, original_query: str, image_urls: list[str]) -> EvaluationResult:
+    """
+    实际执行评估的内部函数 - 直接调用OpenRouter API
+    """
+    # 获取API密钥
+    api_key = os.getenv("OPENROUTER_API_KEY")
+    if not api_key:
+        raise ValueError("OPENROUTER_API_KEY environment variable not set")
+
+    # 构建提示文本
+    prompt_text = EVALUATION_PROMPT_TEMPLATE.format(
+        original_query=original_query,
+        title=post.title,
+        body_text=post.body_text or "",
+        num_images=len(image_urls)
+    )
+
+    # 构建消息内容:文本 + 多张图片
+    content = [{"type": "text", "text": prompt_text}]
+
+    for url in image_urls:
+        content.append({
+            "type": "image_url",
+            "image_url": {"url": url}
+        })
+
+    # 构建API请求
+    payload = {
+        "model": MODEL_NAME,
+        "messages": [{"role": "user", "content": content}],
+        "response_format": {"type": "json_object"}
+    }
+
+    headers = {
+        "Authorization": f"Bearer {api_key}",
+        "Content-Type": "application/json"
+    }
+
+    # 在异步上下文中执行同步请求
+    loop = asyncio.get_event_loop()
+    response = await loop.run_in_executor(
+        None,
+        lambda: requests.post(
+            "https://openrouter.ai/api/v1/chat/completions",
+            headers=headers,
+            json=payload,
+            timeout=API_TIMEOUT
+        )
+    )
+
+    # 检查响应
+    if response.status_code != 200:
+        raise Exception(f"OpenRouter API error: {response.status_code} - {response.text[:200]}")
+
+    # 解析响应
+    result = response.json()
+    content_text = result["choices"][0]["message"]["content"]
+
+    # 去除Markdown代码块标记(Gemini即使设置了json_object也会返回带```json标记的内容)
+    content_text = content_text.strip()
+    if content_text.startswith("```json"):
+        content_text = content_text[7:]
+    elif content_text.startswith("```"):
+        content_text = content_text[3:]
+    if content_text.endswith("```"):
+        content_text = content_text[:-3]
+    content_text = content_text.strip()
+
+    # 解析JSON
+    evaluation_data = json.loads(content_text)
+
+    # 构建EvaluationResult
+    evaluation = EvaluationResult(
+        is_knowledge=evaluation_data.get("is_knowledge", False),
+        knowledge_reason=evaluation_data.get("knowledge_reason", ""),
+        relevance_score=evaluation_data.get("relevance_score", 0.0),
+        relevance_reason=evaluation_data.get("relevance_reason", "")
+    )
+
+    return evaluation
+
+
+async def evaluate_all_posts(
+    posts: list,  # list[Post]
+    original_query: str,
+    max_concurrent: int = MAX_CONCURRENT_EVALUATIONS
+) -> dict[str, EvaluationResult]:
+    """
+    批量评估多个帖子(带并发控制)
+
+    Args:
+        posts: Post对象列表
+        original_query: 原始问题
+        max_concurrent: 最大并发数
+
+    Returns:
+        dict: {note_id: EvaluationResult}
+    """
+    semaphore = asyncio.Semaphore(max_concurrent)
+
+    print(f"\n开始批量评估 {len(posts)} 个帖子(并发限制: {max_concurrent})...")
+
+    tasks = [evaluate_post(post, original_query, semaphore) for post in posts]
+    results = await asyncio.gather(*tasks)
+
+    # 构建字典(过滤None)
+    evaluation_dict = {}
+    success_count = 0
+    for i, evaluation in enumerate(results):
+        if evaluation is not None:
+            evaluation_dict[posts[i].note_id] = evaluation
+            success_count += 1
+
+    print(f"批量评估完成: 成功 {success_count}/{len(posts)}")
+
+    return evaluation_dict
+
+
+def get_relevance_level(score: float) -> str:
+    """
+    根据相关性分数获取分级标签
+
+    Args:
+        score: 相关性分数(0-1)
+
+    Returns:
+        分级标签: "高度相关" | "中度相关" | "低度相关"
+    """
+    if score >= 0.7:
+        return "高度相关"
+    elif score >= 0.4:
+        return "中度相关"
+    else:
+        return "低度相关"
+
+
+# ============================================================================
+# 辅助函数
+# ============================================================================
+
+def apply_evaluation_to_post(post, evaluation: EvaluationResult):
+    """
+    将评估结果应用到Post对象
+
+    Args:
+        post: Post对象
+        evaluation: EvaluationResult对象
+    """
+    post.is_knowledge = evaluation.is_knowledge
+    post.knowledge_reason = evaluation.knowledge_reason
+    post.relevance_score = evaluation.relevance_score
+    post.relevance_level = get_relevance_level(evaluation.relevance_score)
+    post.relevance_reason = evaluation.relevance_reason
+    post.evaluation_time = datetime.now().isoformat()

+ 0 - 193
sug_v1.py

@@ -1,193 +0,0 @@
-import asyncio
-
-from agents import Agent, Runner, function_tool
-from lib.my_trace import set_trace
-from lib.utils import read_file_as_string
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-@function_tool
-def get_query_suggestions(query: str):
-    """Fetch search recommendations from Xiaohongshu."""
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-    query_suggestions = xiaohongshu_api.get_recommendations(keyword=query)['result']['data']['data']
-    return query_suggestions
-
-@function_tool
-def modify_query(original_query: str, operation_type: str, new_query: str, reason: str):
-    """
-    Modify the search query with a specific operation.
-
-    Args:
-        original_query: The original query before modification
-        operation_type: Type of modification - must be one of: "简化", "扩展", "替换", "组合"
-        new_query: The modified query after applying the operation
-        reason: Detailed explanation of why this modification was made and what insight from previous suggestions led to this change
-
-    Returns:
-        A dict containing the modification record and the new query to use for next search
-    """
-    operation_types = ["简化", "扩展", "替换", "组合"]
-    if operation_type not in operation_types:
-        return {
-            "status": "error",
-            "message": f"Invalid operation_type. Must be one of: {', '.join(operation_types)}"
-        }
-
-    modification_record = {
-        "original_query": original_query,
-        "operation_type": operation_type,
-        "new_query": new_query,
-        "reason": reason,
-    }
-
-    return {
-        "status": "success",
-        "modification_record": modification_record,
-        "new_query": new_query,
-        "message": f"Query modified successfully. Use '{new_query}' for the next search."
-    }
-
-insrtuctions = """
-你是一个专业的搜索query优化专家,擅长通过动态探索找到最符合用户搜索习惯的query。
-
-## 核心任务
-给定原始问题,通过迭代调用搜索推荐接口(get_query_suggestions),找到与原始问题语义等价且更符合平台用户搜索习惯的推荐query。
-
-## 工作流程
-
-### 1. 理解原始问题
-- 仔细阅读<需求上下文>和<当前问题>
-- 提取问题的核心需求和关键概念
-- 明确问题的本质意图(what)、应用场景(where)、实现方式(how)
-
-### 2. 动态探索策略
-采用类似人类搜索的迭代探索方式:
-
-**第一轮尝试:**
-- 使用原始问题直接调用 get_query_suggestions(query="原始问题")
-- 仔细分析返回的推荐词列表
-- 判断是否有与原始问题等价的推荐词
-
-**后续迭代:**
-如果推荐词不满足要求,必须先调用 modify_query 函数记录修改,然后再次搜索:
-
-**工具使用流程:**
-1. 调用 modify_query(original_query, operation_type, new_query, reason)
-2. 使用返回的 new_query 调用 get_query_suggestions
-3. 分析新的推荐词列表
-4. 如果仍不满足,重复步骤1-3
-
-**四种操作类型(operation_type):**
-- **简化**:删除冗余词汇,提取核心关键词
-  - 示例:modify_query("快速进行图片背景移除和替换", "简化", "图片背景移除", "原始query过于冗长,'快速进行'和'和替换'是修饰词,核心需求是'图片背景移除'")
-
-- **扩展**:添加场景、平台、工具类型等限定词
-  - 示例:modify_query("图片背景移除", "扩展", "在线图片背景移除工具", "从推荐词看用户更关注具体工具,添加'在线'和'工具'限定词可能更符合搜索习惯")
-
-- **替换**:使用同义词、行业术语或口语化表达
-  - 示例:modify_query("背景移除", "替换", "抠图", "推荐词中出现多个口语化表达,'抠图'是用户更常用的说法")
-
-- **组合**:调整关键词顺序或组合方式
-  - 示例:modify_query("图片背景移除", "组合", "抠图换背景", "调整表达方式,结合推荐词中高频出现的'换背景'概念")
-
-**每次修改的reason必须包含:**
-- 上一轮推荐词给你的启发(如"推荐词中多次出现'抠图'一词")
-- 为什么这样修改更符合平台用户习惯
-- 与原始问题的关系(确保核心意图不变)
-
-### 3. 等价性判断标准
-推荐词满足以下条件即可视为"与原始问题等价":
-
-**语义等价:**
-- 能够回答或解决原始问题的核心需求
-- 涵盖原始问题的关键功能或场景
-
-**搜索有效性:**
-- 是平台真实推荐的query(来自推荐接口)
-- 大概率能找到相关结果(基于平台用户行为)
-
-**可接受的差异:**
-- 表达方式不同但含义相同(如"背景移除" vs "抠图")
-- 范围略有调整但核心不变(如"图片背景移除" vs "图片抠图工具")
-
-### 4. 迭代终止条件
-- **成功终止**:找到至少一个与原始问题等价的推荐query
-- **尝试上限**:最多迭代5轮,避免无限循环
-- **无推荐词**:推荐接口返回空列表或错误
-
-### 5. 输出要求
-成功找到等价query时,输出格式:
-```
-原始问题:[原问题]
-
-优化后的query:[最终找到的等价推荐query]
-
-探索路径:
-1. 第1轮:原始query "[query1]"
-   - 推荐词:[列出关键推荐词]
-   - 判断:不满足,[简要说明原因]
-
-2. 第2轮:modify_query("[query1]", "简化", "[query2]", "[reason]")
-   - 推荐词:[列出关键推荐词]
-   - 判断:不满足,[简要说明原因]
-
-3. 第3轮:modify_query("[query2]", "替换", "[query3]", "[reason]")
-   - 推荐词:[列出关键推荐词]
-   - 判断:满足!找到等价query "[最终query]"
-
-推荐理由:
-- 该query来自平台官方推荐,大概率有结果
-- 与原始问题语义等价:[具体说明]
-- 更符合用户搜索习惯:[具体说明]
-```
-
-未找到等价query时,输出:
-```
-原始问题:[原问题]
-
-探索结果:未找到完全等价的推荐query(已尝试[N]轮)
-
-尝试过的query及修改记录:
-1. "[query1]" (原始)
-2. "[query2]" (简化:[reason])
-3. "[query3]" (替换:[reason])
-...
-
-各轮推荐词分析:
-- 第1轮推荐词偏向:[分析]
-- 第2轮推荐词偏向:[分析]
-...
-
-建议:
-[基于探索结果给出建议,如:
-- 直接使用原问题搜索
-- 使用尝试过的某个query(虽然不完全等价但最接近)
-- 调整搜索策略或平台]
-```
-
-## 注意事项
-- **第一轮必须使用原始问题**:直接调用 get_query_suggestions(query="原始问题")
-- **后续修改必须调用 modify_query**:不能直接用新query调用 get_query_suggestions,必须先通过 modify_query 记录修改
-- **每次调用 get_query_suggestions 后必须仔细分析**:列出关键推荐词,分析它们的特点和偏向
-- **修改要有理有据**:reason参数必须详细说明基于什么推荐词反馈做出此修改
-- **保持核心意图不变**:每次修改都要确认与原始问题的等价性
-- **优先选择简洁、口语化的推荐词**:如果多个推荐词都满足,选择最符合用户习惯的
-""".strip()
-
-agent = Agent(
-    name="Query Optimization Agent",
-    instructions=insrtuctions,
-    tools=[get_query_suggestions, modify_query],
-)
-
-
-async def main():
-    set_trace()
-    user_input = read_file_as_string('input/kg_v1_single.md')
-    result = await Runner.run(agent, input=user_input)
-    print(result.final_output)
-    # The weather in Tokyo is sunny.
-
-
-if __name__ == "__main__":
-    asyncio.run(main())

+ 0 - 316
sug_v1_1.py

@@ -1,316 +0,0 @@
-import asyncio
-
-from agents import Agent, Runner, function_tool
-from lib.my_trace import set_trace
-from lib.utils import read_file_as_string
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-@function_tool
-def get_query_suggestions(query: str):
-    """Fetch search recommendations from Xiaohongshu."""
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-    query_suggestions = xiaohongshu_api.get_recommendations(keyword=query)['result']['data']['data']
-    return query_suggestions
-
-@function_tool
-def modify_query(original_query: str, operation_type: str, new_query: str, reason: str):
-    """
-    Modify the search query with a specific operation.
-
-    Args:
-        original_query: The original query before modification
-        operation_type: Type of modification - must be one of: "简化", "扩展", "替换", "组合"
-        new_query: The modified query after applying the operation
-        reason: Detailed explanation of why this modification was made and what insight from previous suggestions led to this change
-
-    Returns:
-        A dict containing the modification record and the new query to use for next search
-    """
-    operation_types = ["简化", "扩展", "替换", "组合"]
-    if operation_type not in operation_types:
-        return {
-            "status": "error",
-            "message": f"Invalid operation_type. Must be one of: {', '.join(operation_types)}"
-        }
-
-    modification_record = {
-        "original_query": original_query,
-        "operation_type": operation_type,
-        "new_query": new_query,
-        "reason": reason,
-    }
-
-    return {
-        "status": "success",
-        "modification_record": modification_record,
-        "new_query": new_query,
-        "message": f"Query modified successfully. Use '{new_query}' for the next search."
-    }
-
-insrtuctions = """
-你是一个专业的搜索query优化专家,擅长通过动态探索找到最符合用户搜索习惯的query。
-
-## 核心任务
-给定原始问题,通过迭代调用搜索推荐接口(get_query_suggestions),找到与原始问题语义等价且更符合平台用户搜索习惯的推荐query。
-
-## 工作流程
-
-### 1. 理解原始问题
-- 仔细阅读<需求上下文>和<当前问题>
-- 提取问题的核心需求和关键概念
-- 明确问题的本质意图(what)、应用场景(where)、实现方式(how)
-
-### 2. 动态探索策略
-采用类似人类搜索的迭代探索方式:
-
-**第一轮尝试(优先不拆分):**
-- 使用完整的原始问题直接调用 get_query_suggestions(query="原始问题")
-- 仔细分析返回的推荐词列表
-- 判断是否有与原始问题**完整等价**的推荐词
-- **关键判断**:推荐词是否能覆盖原始问题的所有核心需求
-  - 如果推荐词能覆盖所有需求 → 继续单query探索
-  - 如果推荐词始终只覆盖部分需求 → 考虑拆分策略(见下文)
-
-**后续迭代:**
-如果推荐词不满足要求,必须先调用 modify_query 函数记录修改,然后再次搜索:
-
-**工具使用流程:**
-1. 调用 modify_query(original_query, operation_type, new_query, reason)
-2. 使用返回的 new_query 调用 get_query_suggestions
-3. 分析新的推荐词列表
-4. 如果仍不满足,重复步骤1-3
-
-**四种操作类型(operation_type):**
-
-- **简化**:删除冗余词汇,提取核心关键词
-  - 适用场景:原始query过于冗长,包含修饰词、限定词或多余描述
-  - 策略:保留表达核心需求的名词和动词,删除修饰性的形容词、副词、疑问词、方法词等辅助成分
-  - 理由示例:"原始query包含多个辅助词汇,推荐词显示用户倾向于使用简短的核心表达"
-
-- **扩展**:添加场景、平台、工具类型等限定词
-  - 适用场景:推荐词显示用户关注特定的使用场景、平台环境或资源类型
-  - 策略:根据推荐词的高频特征,添加相应的场景限定词、平台限定词、资源类型词等
-  - 理由示例:"推荐词中高频出现某类限定词,添加该限定词更符合用户搜索意图"
-
-- **替换**:使用同义词、行业术语或口语化表达
-  - 适用场景:推荐词中出现原始query的同义词或更常用的表达方式
-  - 策略:观察推荐词中的高频词汇,用口语化或行业通用表达替换书面语
-  - 理由示例:"推荐词中多次出现某个同义词,说明该词是平台用户的常用表达"
-
-- **组合**:调整关键词顺序或组合方式
-  - 适用场景:推荐词显示用户使用不同的关键词组合方式
-  - 策略:调整词序、拆分或合并关键词
-  - 理由示例:"推荐词中出现不同的词序组合,调整后可能更符合搜索习惯"
-
-**每次修改的reason必须包含:**
-- 上一轮推荐词的具体特征(如"推荐词中多次出现某个特定词汇"、"推荐词偏向某个方向")
-- 为什么这样修改更符合平台用户习惯(基于推荐词反馈)
-- 与原始问题的关系(确保核心意图不变)
-
-**何时考虑拆分策略:**
-只有当满足以下条件时,才考虑将原始问题拆分成多个query:
-1. 已经尝试了多轮探索(至少3-4轮)
-2. 推荐词的反馈始终显示:只覆盖原始问题的部分需求,无法找到完整覆盖的等价query
-3. 原始问题确实包含多个相对独立的功能需求(如"A和B"、"移除和替换")
-
-**拆分后的探索方式:**
-1. 识别原始问题包含的各个子需求
-2. 为每个子需求独立进行完整的探索流程(从第一轮开始)
-3. 最终输出多个query,每个覆盖一个子需求
-4. 验证所有子需求的组合能否完整覆盖原始问题
-
-### 3. 等价性判断标准
-
-**前提条件:**
-所有推荐词都来自 get_query_suggestions 返回,已经是平台基于用户行为的真实推荐,具有搜索有效性。
-
-**等价性判断(核心标准):**
-在推荐词列表中,判断某个推荐词是否与原始问题等价,标准如下:
-
-**必须满足 - 语义等价:**
-- 能够回答或解决原始问题的核心需求
-- 涵盖原始问题的关键功能、目标或场景
-- 核心概念一致,虽然表达方式可能不同
-
-**可接受的差异:**
-- 表达方式:书面语 vs 口语化表达
-- 词汇选择:专业术语 vs 通俗说法、同义词替换
-- 范围调整:核心功能一致,但加了具体场景、工具类型等限定词
-- 详略程度:简化版或扩展版,但核心不变
-
-**不可接受的差异:**
-- 核心需求改变(如原问题要"移除",推荐词是"添加")
-- 功能偏离(如原问题要"工具",推荐词是"教程")
-- 领域错位(如原问题是"图片处理",推荐词是"视频处理")
-
-### 4. 迭代终止条件
-- **成功终止**:找到至少一个与原始问题等价的推荐query
-- **尝试上限**:最多迭代5轮,避免无限循环
-- **无推荐词**:推荐接口返回空列表或错误
-
-### 5. 输出要求
-
-**成功找到等价query时:**
-
-**情况1 - 单一query即可覆盖:**
-```
-✓ 搜索成功
-
-原始问题:[完整的原始问题]
-问题分析:该问题可以用单一query充分表达
-
-优化后的query:[找到的等价推荐query]
-找到轮次:第[N]轮
-
-探索过程:
-第1轮 - 原始query
-  Query: [原始问题]
-  推荐词: [列出5-10个代表性推荐词]
-  分析: [推荐词的特点和偏向]
-  判断: 未找到等价query
-
-第2轮 - [操作类型]
-  修改: modify_query(..., operation_type="[类型]", ...)
-  Reason: [详细的修改理由]
-  Query: [新query]
-  推荐词: [列出代表性推荐词]
-  分析: [推荐词的特点变化]
-  判断: 未找到等价query
-
-第N轮 - [操作类型]
-  修改: modify_query(..., operation_type="[类型]", ...)
-  Reason: [详细的修改理由]
-  Query: [最终query]
-  推荐词: [包含等价query的推荐词列表]
-  分析: [为什么其中某个推荐词等价]
-  判断: ✓ 找到等价query "[等价的推荐词]"
-
-等价性说明:
-- 语义等价:[说明为什么与原始问题等价]
-- 用户习惯:[说明为什么更符合搜索习惯]
-- 搜索有效性:来自平台推荐,基于真实用户行为
-```
-
-**情况2 - 需要多个query覆盖(少数情况):**
-仅当尝试多轮探索后,推荐词反馈明确显示无法用单query覆盖所有需求时使用
-```
-✓ 搜索成功(多query)
-
-原始问题:[完整的原始问题]
-问题分析:经过[N]轮探索,推荐词始终只覆盖部分需求,因此拆分成多个query
-
-需求拆分:
-子需求1:[提取的第一个子需求]
-子需求2:[提取的第二个子需求]
-子需求3:[提取的第三个子需求](如有)
-...
-
-优化后的query列表:
-━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-Query 1: [等价query1]
-  覆盖需求:子需求1
-  找到轮次:第[N]轮
-  等价性说明:[为什么与子需求1等价]
-
-Query 2: [等价query2]
-  覆盖需求:子需求2
-  找到轮次:第[M]轮
-  等价性说明:[为什么与子需求2等价]
-
-Query 3: [等价query3]
-  覆盖需求:子需求3
-  找到轮次:第[K]轮
-  等价性说明:[为什么与子需求3等价]
-...
-━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-
-拆分决策依据:
-- 为什么选择拆分:[说明尝试单query时推荐词的反馈情况]
-- 哪些轮次的推荐词显示只覆盖部分需求:[具体分析]
-
-完整性验证:
-- 原始问题包含的所有核心需求是否都被覆盖:✓
-- 各query之间是否有重叠或遗漏:[分析]
-
-每个子需求的探索过程:
-[展示每个子需求的完整探索路径]
-```
-
-**未找到等价query时:**
-```
-✗ 搜索未找到等价query
-
-原始问题:[完整的原始问题]
-探索轮次:已尝试[N]轮(达到上限或其他终止原因)
-
-探索记录:
-第1轮: [query1] → 推荐词偏向[方向]
-第2轮: [query2] ([操作类型]) → 推荐词偏向[方向]
-第3轮: [query3] ([操作类型]) → 推荐词偏向[方向]
-...
-
-整体分析:
-- 推荐词的共同特点:[综合分析]
-- 与原始问题的gap:[说明差异]
-- 可能的原因:[推测为什么找不到]
-
-建议:
-1. [最可行的替代方案]
-2. [其他建议]
-```
-
-## 注意事项
-
-**工作原则:**
-- **数据驱动**:所有决策必须基于推荐词的实际反馈,而非主观猜测
-- **优先整体**:始终优先尝试用单一query覆盖完整的原始问题
-- **灵活调整**:不同问题需要不同策略,观察推荐词特点后灵活选择操作类型
-- **保持等价性**:探索过程中始终确保与原始问题的核心意图一致
-- **按需拆分**:只有当推荐词反馈明确显示单query无法覆盖时,才基于实际情况拆分成多个query
-
-**操作规范:**
-- **第一轮必须使用原始问题**:直接调用 get_query_suggestions(query="原始问题")
-- **后续修改必须调用 modify_query**:不能直接用新query调用 get_query_suggestions,必须先记录修改
-- **仔细分析推荐词**:每次获取推荐词后,列出代表性词汇,分析特点、偏向、高频词
-- **详细说明理由**:reason参数必须说明基于推荐词的哪些具体观察做出修改
-- **选择最优结果**:如果多个推荐词都等价,优先选择最简洁、最口语化的
-
-**策略提示:**
-- **优先单query探索**:
-  - 始终先尝试用单一query覆盖完整的原始问题
-  - 即使原始问题包含"和"、"及"等连接词,也优先尝试作为整体探索
-  - 只有在推荐词反馈明确显示无法用单query覆盖时,才考虑拆分
-- **探索策略灵活调整**:
-  - 不同领域的问题需要不同的探索路径
-  - 专业术语类问题可能需要"替换"为口语化表达
-  - 描述性问题可能需要"简化"提取核心词
-  - 抽象概念可能需要"扩展"添加具体场景
-  - 观察推荐词的变化趋势,及时调整策略方向
-- **何时考虑拆分**:
-  - 已尝试3-4轮仍无法找到完整覆盖的等价query
-  - 推荐词始终只偏向原始问题的某一部分需求
-  - 此时才启动拆分策略,为各子需求分别探索
-""".strip()
-
-agent = Agent(
-    name="Query Optimization Agent",
-    instructions=insrtuctions,
-    tools=[get_query_suggestions, modify_query],
-)
-
-
-async def main():
-    set_trace()
-    # user_input = read_file_as_string('input/kg_v1_single.md')
-    user_input = """
-<当前问题>
-如何构建帖子标题和正文,能通过拟人的手法以猫咪的情绪引发读者产生共鸣?
-</当前问题>
-""".strip()
-    result = await Runner.run(agent, input=user_input)
-    print(result.final_output)
-    # The weather in Tokyo is sunny.
-
-
-if __name__ == "__main__":
-    asyncio.run(main())

+ 0 - 179
sug_v1_2.py

@@ -1,179 +0,0 @@
-import asyncio
-
-from agents import Agent, Runner, function_tool
-from lib.my_trace import set_trace
-from lib.utils import read_file_as_string
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-@function_tool
-def get_query_suggestions(query: str):
-    """Fetch search recommendations from Xiaohongshu."""
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-    query_suggestions = xiaohongshu_api.get_recommendations(keyword=query)['result']['data']['data']
-    return query_suggestions
-
-@function_tool
-def modify_query(original_query: str, operation_type: str, new_query: str, reason: str):
-    """
-    Modify the search query with a specific operation.
-
-    Args:
-        original_query: The original query before modification
-        operation_type: Type of modification - must be one of: "简化", "扩展", "替换", "组合"
-        new_query: The modified query after applying the operation
-        reason: Detailed explanation of why this modification was made and what insight from previous suggestions led to this change
-
-    Returns:
-        A dict containing the modification record and the new query to use for next search
-    """
-    operation_types = ["简化", "扩展", "替换", "组合"]
-    if operation_type not in operation_types:
-        return {
-            "status": "error",
-            "message": f"Invalid operation_type. Must be one of: {', '.join(operation_types)}"
-        }
-
-    modification_record = {
-        "original_query": original_query,
-        "operation_type": operation_type,
-        "new_query": new_query,
-        "reason": reason,
-    }
-
-    return {
-        "status": "success",
-        "modification_record": modification_record,
-        "new_query": new_query,
-        "message": f"Query modified successfully. Use '{new_query}' for the next search."
-    }
-
-insrtuctions = """
-你是一个专业的搜索query优化专家,擅长通过动态探索找到最符合用户搜索习惯的query。
-
-## 核心任务
-给定原始问题,通过迭代调用搜索推荐接口(get_query_suggestions),找到与原始问题语义等价且更符合平台用户搜索习惯的推荐query。
-
-## 工作流程
-
-### 1. 理解原始问题
-- 仔细阅读<需求上下文>和<当前问题>
-- 提取问题的核心需求和关键概念
-- 明确问题的本质意图(what)、应用场景(where)、实现方式(how)
-
-### 2. 动态探索策略
-采用类似人类搜索的迭代探索方式:
-
-**第一轮尝试:**
-- 使用原始问题直接调用 get_query_suggestions(query="原始问题")
-- 仔细分析返回的推荐词列表
-- 判断是否有与原始问题等价的推荐词
-
-**后续迭代:**
-如果推荐词不满足要求,必须先调用 modify_query 函数记录修改,然后再次搜索:
-
-**工具使用流程:**
-1. 调用 modify_query(original_query, operation_type, new_query, reason)
-2. 使用返回的 new_query 调用 get_query_suggestions
-3. 分析新的推荐词列表
-4. 如果仍不满足,重复步骤1-3
-
-**四种操作类型(operation_type):**
-- **简化**:删除冗余词汇,提取核心关键词
-- **扩展**
-- **替换**:使用同义词、行业术语或口语化表达
-- **组合**:调整关键词顺序或组合方式
-
-**每次修改的reason必须包含:**
-- 上一轮推荐词给你的启发
-- 为什么这样修改更符合平台用户习惯
-- 与原始问题的关系(确保核心意图不变)
-
-### 3. 等价性判断标准
-推荐词满足以下条件即可视为"与原始问题等价":
-
-**语义等价:**
-- 能够回答或解决原始问题的核心需求
-- 涵盖原始问题的关键功能或场景
-
-
-### 4. 迭代终止条件
-- **成功终止**:找到至少一个与原始问题等价的推荐query
-- **尝试上限**:最多迭代5轮,避免无限循环
-- **无推荐词**:推荐接口返回空列表或错误
-
-### 5. 输出要求
-成功找到等价query时,输出格式:
-```
-原始问题:[原问题]
-
-优化后的query:[最终找到的等价推荐query]
-
-探索路径:
-1. 第1轮:原始query "[query1]"
-   - 推荐词:[列出关键推荐词]
-   - 判断:不满足,[简要说明原因]
-
-2. 第2轮:modify_query("[query1]", "简化", "[query2]", "[reason]")
-   - 推荐词:[列出关键推荐词]
-   - 判断:不满足,[简要说明原因]
-
-3. 第3轮:modify_query("[query2]", "替换", "[query3]", "[reason]")
-   - 推荐词:[列出关键推荐词]
-   - 判断:满足!找到等价query "[最终query]"
-
-推荐理由:
-- 该query来自平台官方推荐,大概率有结果
-- 与原始问题语义等价:[具体说明]
-- 更符合用户搜索习惯:[具体说明]
-```
-
-未找到等价query时,输出:
-```
-原始问题:[原问题]
-
-探索结果:未找到完全等价的推荐query(已尝试[N]轮)
-
-尝试过的query及修改记录:
-1. "[query1]" (原始)
-2. "[query2]" (简化:[reason])
-3. "[query3]" (替换:[reason])
-...
-
-各轮推荐词分析:
-- 第1轮推荐词偏向:[分析]
-- 第2轮推荐词偏向:[分析]
-...
-
-建议:
-[基于探索结果给出建议,如:
-- 直接使用原问题搜索
-- 使用尝试过的某个query(虽然不完全等价但最接近)
-- 调整搜索策略或平台]
-```
-
-## 注意事项
-- **第一轮必须使用原始问题**:直接调用 get_query_suggestions(query="原始问题")
-- **后续修改必须调用 modify_query**:不能直接用新query调用 get_query_suggestions,必须先通过 modify_query 记录修改
-- **每次调用 get_query_suggestions 后必须仔细分析**:列出关键推荐词,分析它们的特点和偏向
-- **修改要有理有据**:reason参数必须详细说明基于什么推荐词反馈做出此修改
-- **保持核心意图不变**:每次修改都要确认与原始问题的等价性
-- **优先选择简洁、口语化的推荐词**:如果多个推荐词都满足,选择最符合用户习惯的
-""".strip()
-
-agent = Agent(
-    name="Query Optimization Agent",
-    instructions=insrtuctions,
-    tools=[get_query_suggestions, modify_query],
-)
-
-
-async def main():
-    set_trace()
-    user_input = read_file_as_string('input/kg_v1_single.md')
-    result = await Runner.run(agent, input=user_input)
-    print(result.final_output)
-    # The weather in Tokyo is sunny.
-
-
-if __name__ == "__main__":
-    asyncio.run(main())

+ 0 - 312
sug_v2.py

@@ -1,312 +0,0 @@
-import asyncio
-import json
-import os
-import argparse
-from datetime import datetime
-
-from agents import Agent, Runner, function_tool
-from lib.my_trace import set_trace
-from typing import Literal
-from dataclasses import dataclass
-from pydantic import BaseModel, Field
-
-
-from lib.utils import read_file_as_string
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-from agents import Agent, RunContextWrapper, Runner, function_tool
-
-from pydantic import BaseModel, Field
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射,如 {'context_file': '...', 'q_file': '...'}")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-    # 中间数据记录 - 按时间顺序记录所有操作
-    operations_history: list[dict] = Field(default_factory=list, description="记录所有操作的历史,包括 get_query_suggestions 和 modify_query")
-    # 最终输出结果
-    final_output: str | None = Field(default=None, description="Agent的最终输出结果")
-
-
-eval_insrtuctions = """
-你是一个专业的评估专家,负责评估给定的搜索query是否满足原始问题和需求,给出出评分和简明扼要的理由。
-"""
-
-@dataclass
-class EvaluationFeedback:
-    reason: str=Field(..., description="简明扼要的理由")
-    score: float=Field(..., description="评估结果,1表示等价,0表示不等价,中间值表示部分等价")
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_insrtuctions,
-    output_type=EvaluationFeedback,
-)
-
-@function_tool
-async def get_query_suggestions(wrapper: RunContextWrapper[RunContext], query: str):
-    """Fetch search recommendations from Xiaohongshu."""
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-    query_suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-    print(query_suggestions)
-    async def evaluate_single_query(q_sug: str, q_with_context: str):
-        """Evaluate a single query suggestion."""
-        eval_input = f"""
-{q_with_context}
-<待评估的推荐query>
-{q_sug}
-</待评估的推荐query>
-        """
-        evaluator_result = await Runner.run(evaluator, eval_input)
-        result: EvaluationFeedback = evaluator_result.final_output
-        return {
-            "query": q_sug,
-            "score": result.score,
-            "reason": result.reason,
-        }
-
-    # 并发执行所有评估任务
-    q_with_context = wrapper.context.q_with_context
-    res = []
-    if query_suggestions:
-        res = await asyncio.gather(*[evaluate_single_query(q_sug, q_with_context) for q_sug in query_suggestions])
-    else:
-        res = '未返回任何推荐词'
-
-    # 记录到 RunContext
-    wrapper.context.operations_history.append({
-        "operation_type": "get_query_suggestions",
-        "timestamp": datetime.now().isoformat(),
-        "query": query,
-        "suggestions": query_suggestions,
-        "evaluations": res,
-    })
-
-    return res
-
-@function_tool
-def modify_query(wrapper: RunContextWrapper[RunContext], original_query: str, operation_type: str, new_query: str, reason: str):
-    """
-    Modify the search query with a specific operation.
-
-    Args:
-        original_query: The original query before modification
-        operation_type: Type of modification - must be one of: "简化", "扩展", "替换", "组合"
-        new_query: The modified query after applying the operation
-        reason: Detailed explanation of why this modification was made and what insight from previous suggestions led to this change
-
-    Returns:
-        A dict containing the modification record and the new query to use for next search
-    """
-    operation_types = ["简化", "扩展", "替换", "组合"]
-    if operation_type not in operation_types:
-        return {
-            "status": "error",
-            "message": f"Invalid operation_type. Must be one of: {', '.join(operation_types)}"
-        }
-
-    modification_record = {
-        "original_query": original_query,
-        "operation_type": operation_type,
-        "new_query": new_query,
-        "reason": reason,
-    }
-
-    # 记录到 RunContext
-    wrapper.context.operations_history.append({
-        "operation_type": "modify_query",
-        "timestamp": datetime.now().isoformat(),
-        "modification_type": operation_type,
-        "original_query": original_query,
-        "new_query": new_query,
-        "reason": reason,
-    })
-
-    return {
-        "status": "success",
-        "modification_record": modification_record,
-        "new_query": new_query,
-        "message": f"Query modified successfully. Use '{new_query}' for the next search."
-    }
-
-insrtuctions = """
-你是一个专业的搜索query优化专家,擅长通过动态探索找到最符合用户搜索习惯的query。
-
-## 核心任务
-给定原始问题,通过迭代调用搜索推荐接口(get_query_suggestions),找到与原始问题语义等价且更符合平台用户搜索习惯的推荐query。
-
-## 重要说明
-- **你不需要自己评估query的等价性**
-- get_query_suggestions 函数内部已集成评估子agent,会自动对每个推荐词进行评估
-- 返回结果包含:query(推荐词)、score(评分,1表示等价,0表示不等价)、reason(评估理由)
-- **你的职责是分析评估结果,做出决策和策略调整**
-
-## 防止幻觉 - 关键原则
-- **严禁编造数据**:只能基于 get_query_suggestions 实际返回的结果进行分析
-- **空结果处理**:如果返回的列表为空([]),必须明确说明"未返回任何推荐词"
-- **不要猜测**:在 modify_query 的 reason 中,不能引用不存在的推荐词或评分
-- **如实记录**:每次分析都要如实反映实际返回的数据
-
-## 工作流程
-
-### 1. 理解原始问题
-- 仔细阅读<需求上下文>和<当前问题>
-- 提取问题的核心需求和关键概念
-- 明确问题的本质意图(what)、应用场景(where)、实现方式(how)
-
-### 2. 动态探索策略
-
-**第一轮尝试:**
-- 使用原始问题直接调用 get_query_suggestions(query="原始问题")
-- **检查返回结果**:
-  - 如果返回空列表 []:说明"该query未返回任何推荐词",需要简化或替换query
-  - 如果有推荐词:查看每个推荐词的 score 和 reason
-- **做出判断**:是否有 score >= 0.8 的高分推荐词?
-
-**后续迭代:**
-如果没有高分推荐词(或返回空列表),必须先调用 modify_query 记录修改,然后再次搜索:
-
-**工具使用流程:**
-1. **分析评估反馈**(必须基于实际返回的数据):
-   - **情况A - 返回空列表**:
-     * 在 reason 中说明:"第X轮未返回任何推荐词,可能是query过于复杂或生僻"
-     * 不能编造任何推荐词或评分
-   - **情况B - 有推荐词但无高分**:
-     * 哪些推荐词得分较高?具体是多少分?评估理由是什么?
-     * 哪些推荐词偏离了原问题?如何偏离的?
-     * 推荐词整体趋势是什么?(过于泛化/具体化/领域偏移等)
-
-2. **决策修改策略**:基于实际评估反馈,调用 modify_query(original_query, operation_type, new_query, reason)
-   - reason 必须引用具体的数据,不能编造
-
-3. 使用返回的 new_query 调用 get_query_suggestions
-
-4. 分析新的评估结果,如果仍不满足,重复步骤1-3
-
-**四种操作类型(operation_type):**
-- **简化**:删除冗余词汇,提取核心关键词(当推荐词过于发散时)
-- **扩展**:添加限定词或场景描述(当推荐词过于泛化时)
-- **替换**:使用同义词、行业术语或口语化表达(当推荐词偏离核心时)
-- **组合**:调整关键词顺序或组合方式(当推荐词结构不合理时)
-
-**每次修改的reason必须包含:**
-- 上一轮评估结果的关键发现(引用具体的score和reason)
-- 基于评估反馈,为什么这样修改
-- 预期这次修改会带来什么改进
-
-### 3. 决策标准
-- **score >= 0.8**:认为该推荐词与原问题等价,可以作为最终结果
-- **0.5 <= score < 0.8**:部分等价,分析reason看是否可接受
-- **score < 0.5**:不等价,需要继续优化
-
-### 4. 迭代终止条件
-- **成功终止**:找到至少一个 score >= 0.8 的推荐query
-- **尝试上限**:最多迭代5轮,避免无限循环
-- **无推荐词**:推荐接口返回空列表或错误
-
-### 5. 输出要求
-
-**成功找到等价query时,输出格式:**
-```
-原始问题:[原问题]
-优化后的query:[最终找到的等价推荐query]
-评分:[score]
-```
-
-**未找到等价query时,输出格式:**
-```
-原始问题:[原问题]
-结果:未找到完全等价的推荐query
-建议:[简要建议,如:直接使用原问题搜索 或 使用最接近的推荐词]
-```
-
-## 注意事项
-- **第一轮必须使用原始问题**:直接调用 get_query_suggestions(query="原始问题")
-- **后续修改必须调用 modify_query**:不能直接用新query调用 get_query_suggestions
-- **重点关注评估结果**:每次都要仔细分析返回的 score 和 reason
-- **基于数据决策**:修改策略必须基于评估反馈,不能凭空猜测
-- **引用具体评分**:在分析和决策时,引用具体的score数值和reason内容
-- **优先选择高分推荐词**:score >= 0.8 即可认为等价
-- **严禁编造数据**:
-  * 如果返回空列表,必须在 reason 中明确说明"未返回任何推荐词"
-  * 不能引用不存在的推荐词、评分或评估理由
-  * 每次 modify_query 的 reason 必须基于上一轮实际返回的结果
-""".strip()
-
-
-
-
-async def main(input_dir: str):
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]  # 去掉 .py 后缀
-
-    # 日志保存到输入目录的 output/版本/时间戳 目录下
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-    agent = Agent[RunContext](
-        name="Query Optimization Agent",
-        instructions=insrtuctions,
-        tools=[get_query_suggestions, modify_query],
-       
-    )
-    result = await Runner.run(agent, input=q_with_context,  context = run_context,)
-    print(result.final_output)
-
-    # 保存最终输出到 RunContext
-    run_context.final_output = str(result.final_output)
-
-    # 保存 RunContext 到 log_dir
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    args = parser.parse_args()
-
-    asyncio.run(main(args.input_dir))

+ 0 - 311
sug_v2_1.py

@@ -1,311 +0,0 @@
-import asyncio
-import json
-import os
-from datetime import datetime
-
-from agents import Agent, Runner, function_tool
-from lib.my_trace import set_trace
-from typing import Literal
-from dataclasses import dataclass
-from pydantic import BaseModel, Field
-
-
-from lib.utils import read_file_as_string
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-from agents import Agent, RunContextWrapper, Runner, function_tool
-
-from pydantic import BaseModel, Field
-
-
-class RunContext(BaseModel):
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-    # 中间数据记录 - 按时间顺序记录所有操作
-    operations_history: list[dict] = Field(default_factory=list, description="记录所有操作的历史,包括 get_query_suggestions 和 modify_query")
-
-
-eval_insrtuctions = """
-你是一个专业的评估专家,负责评估给定的搜索query是否满足原始问题和需求,给出出评分和简明扼要的理由。
-"""
-
-@dataclass
-class EvaluationFeedback:
-    reason: str=Field(..., description="简明扼要的理由")
-    score: float=Field(..., description="评估结果,1表示等价,0表示不等价,中间值表示部分等价")
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_insrtuctions,
-    output_type=EvaluationFeedback,
-)
-
-@function_tool
-async def get_query_suggestions(wrapper: RunContextWrapper[RunContext], query: str):
-    """Fetch search recommendations from Xiaohongshu."""
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-    query_suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-    print(query_suggestions)
-    async def evaluate_single_query(q_sug: str, q_with_context: str):
-        """Evaluate a single query suggestion."""
-        eval_input = f"""
-{q_with_context}
-<待评估的推荐query>
-{q_sug}
-</待评估的推荐query>
-        """
-        evaluator_result = await Runner.run(evaluator, eval_input)
-        result: EvaluationFeedback = evaluator_result.final_output
-        return {
-            "query": q_sug,
-            "score": result.score,
-            "reason": result.reason,
-        }
-
-    # 并发执行所有评估任务
-    q_with_context = wrapper.context.q_with_context
-    res = []
-    if query_suggestions:
-        res = await asyncio.gather(*[evaluate_single_query(q_sug, q_with_context) for q_sug in query_suggestions])
-    else:
-        res = '未返回任何推荐词'
-
-    # 记录到 RunContext
-    wrapper.context.operations_history.append({
-        "operation_type": "get_query_suggestions",
-        "timestamp": datetime.now().isoformat(),
-        "query": query,
-        "suggestions": query_suggestions,
-        "evaluations": res,
-    })
-
-    return res
-
-@function_tool
-def modify_query(wrapper: RunContextWrapper[RunContext], original_query: str, operation_type: str, new_query: str, reason: str):
-    """
-    Modify the search query with a specific operation.
-
-    Args:
-        original_query: The original query before modification
-        operation_type: Type of modification - must be one of:
-            - "增加": 增加query语句,添加具体化的关键词
-            - "删减": 删减query词,去除冗余词汇
-            - "调序": 调整query词的顺序,更符合用户习惯的词序
-            - "替换": 替换query中的某个词,使用同义词或专业术语
-        new_query: The modified query after applying the operation
-        reason: Detailed explanation of why this modification was made and what insight from previous suggestions led to this change.
-                Must reference specific data from get_query_suggestions results (e.g., scores, reasons, word patterns).
-
-    Returns:
-        A dict containing the modification record and the new query to use for next search
-    """
-    operation_types = ["增加", "删减", "调序", "替换"]
-    if operation_type not in operation_types:
-        return {
-            "status": "error",
-            "message": f"Invalid operation_type. Must be one of: {', '.join(operation_types)}"
-        }
-
-    modification_record = {
-        "original_query": original_query,
-        "operation_type": operation_type,
-        "new_query": new_query,
-        "reason": reason,
-    }
-
-    # 记录到 RunContext
-    wrapper.context.operations_history.append({
-        "operation_type": "modify_query",
-        "timestamp": datetime.now().isoformat(),
-        "modification_type": operation_type,
-        "original_query": original_query,
-        "new_query": new_query,
-        "reason": reason,
-    })
-
-    return {
-        "status": "success",
-        "modification_record": modification_record,
-        "new_query": new_query,
-        "message": f"Query modified successfully. Use '{new_query}' for the next search."
-    }
-
-insrtuctions = """
-你是一个专业的搜索query优化专家,擅长通过动态探索找到最符合用户搜索习惯的query。
-
-## 核心任务
-给定原始问题,通过迭代调用搜索推荐接口(get_query_suggestions),找到与原始问题语义等价且更符合平台用户搜索习惯的推荐query。
-
-## 重要说明
-- **你不需要自己评估query的等价性**
-- get_query_suggestions 函数内部已集成评估子agent,会自动对每个推荐词进行评估
-- 返回结果包含:query(推荐词)、score(评分,1表示等价,0表示不等价)、reason(评估理由)
-- **你的职责是分析评估结果,做出决策和策略调整**
-
-## 防止幻觉 - 关键原则
-- **严禁编造数据**:只能基于 get_query_suggestions 实际返回的结果进行分析
-- **空结果处理**:如果返回的列表为空([]),必须明确说明"未返回任何推荐词"
-- **不要猜测**:在 modify_query 的 reason 中,不能引用不存在的推荐词或评分
-- **如实记录**:每次分析都要如实反映实际返回的数据
-
-## 工作流程
-
-### 1. 理解原始问题
-- 仔细阅读<需求上下文>和<当前问题>
-- 提取问题的核心需求和关键概念
-- 明确问题的本质意图(what)、应用场景(where)、实现方式(how)
-
-### 2. 动态探索策略
-
-**第一轮尝试:**
-- 使用原始问题直接调用 get_query_suggestions(query="原始问题")
-- **检查返回结果**:
-  - 如果返回空列表 []:说明"该query未返回任何推荐词",需要简化或替换query
-  - 如果有推荐词:查看每个推荐词的 score 和 reason
-- **做出判断**:是否有 score >= 0.8 的高分推荐词?
-
-**后续迭代:**
-如果没有高分推荐词(或返回空列表),必须先调用 modify_query 记录修改,然后再次搜索:
-
-**工具使用流程:**
-1. **分析评估反馈**(必须基于实际返回的数据):
-   - **情况A - 返回空列表**:
-     * 在 reason 中说明:"第X轮未返回任何推荐词,可能是query过于复杂或生僻"
-     * 不能编造任何推荐词或评分
-   - **情况B - 有推荐词但无高分**:
-     * 哪些推荐词得分较高?具体是多少分?评估理由是什么?
-     * 哪些推荐词偏离了原问题?如何偏离的?
-     * 推荐词整体趋势是什么?(过于泛化/具体化/领域偏移等)
-
-2. **决策修改策略**:基于实际评估反馈,调用 modify_query(original_query, operation_type, new_query, reason)
-   - reason 必须引用具体的数据,不能编造
-
-3. 使用返回的 new_query 调用 get_query_suggestions
-
-4. 分析新的评估结果,如果仍不满足,重复步骤1-3
-
-**四种操作类型(operation_type)及其详细判定标准:**
-
-**策略1:增加**
-- **判定标准**:
-  * 有效sug列表中,sug_i 的语义比 Query 更具体,且词汇数量 > query 的词汇数量
-  * sug list 中的每个词是 Query 的更具体或细化版本,是能将 Query 具体化描述的关键新词
-  * sug_i 必须包含 Query 的大部分或所有核心关键词,且词汇数量都明显多于query
-  * sug_j 中存在 query 中不存在的关键新词集合(例如:特定平台名称、特定功能词)
-- **使用时机**:当推荐词整体趋势是添加更具体的限定词或场景描述时
-
-**策略2:删减**
-- **判定标准**(满足以下任一条件即可):
-  * 有效sug列表中,大部分 sug 的词汇数量都明显少于 query
-  * 当sug列表为空时
-  * sug 词汇数量明显少于 query,且与 Query 的核心搜索意图一致
-  * 基于评估反馈,识别 query 中 1-2 个词为"非核心冗余词",且删除后不影响核心搜索意图
-- **使用时机**:当原query过于冗长复杂,推荐词都倾向于使用更简洁的表达时
-
-**策略3:调序**
-- **判定标准**:
-  * 有效sug list中,存在多个 sug_i 与 query 拥有完全相同的核心关键词集合,只是词汇排列顺序不同
-  * sug_i 与 query 的词序不同,但 sug_i 的词序变化与 query 的核心语义一致
-  * sug_i 的词序更自然或更符合搜索习惯,且 sug_i 的词序频率高于 query 词的词序
-  * 多个 Sug_List 中的 sug_i 都倾向于使用与 Query 不同但意思一致的词序
-- **使用时机**:当推荐词与原query关键词相同但顺序不同,且新顺序更符合用户习惯时
-
-**策略4:替换**
-- **判定标准**:
-  * 存在 sug_i(来自有效suglist),其与 query 之间仅有 1-2 个核心词不同,但其他词均相同
-  * sug_i 作为整体查询词,其语义与 query 等价或更优
-  * sug_i 与 Query 在大部分词汇上重叠,但在 1-2 个关键位置使用了不同的词
-  * 这些不同的词在语义上是同义、近义或在相关领域中更流行/专业的替代词
-  * 差异词中的新词在 Platform Sug List 中出现频率更高,或被标记为更专业的术语
-- **使用时机**:当推荐词显示某个词的同义词或专业术语更受欢迎时
-
-**每次修改的reason必须包含:**
-- 上一轮评估结果的关键发现(引用具体的score和reason)
-- 基于评估反馈,为什么这样修改
-- 预期这次修改会带来什么改进
-
-### 3. 决策标准
-- **score >= 0.8**:认为该推荐词与原问题等价,可以作为最终结果
-- **0.5 <= score < 0.8**:部分等价,分析reason看是否可接受
-- **score < 0.5**:不等价,需要继续优化
-
-### 4. 迭代终止条件
-- **成功终止**:找到至少一个 score >= 0.8 的推荐query
-- **尝试上限**:最多迭代5轮,避免无限循环
-- **无推荐词**:推荐接口返回空列表或错误
-
-### 5. 输出要求
-
-**成功找到等价query时,输出格式:**
-```
-原始问题:[原问题]
-优化后的query:[最终找到的等价推荐query]
-评分:[score]
-```
-
-**未找到等价query时,输出格式:**
-```
-原始问题:[原问题]
-结果:未找到完全等价的推荐query
-建议:[简要建议,如:直接使用原问题搜索 或 使用最接近的推荐词]
-```
-
-## 注意事项
-- **第一轮必须使用原始问题**:直接调用 get_query_suggestions(query="原始问题")
-- **后续修改必须调用 modify_query**:不能直接用新query调用 get_query_suggestions
-- **重点关注评估结果**:每次都要仔细分析返回的 score 和 reason
-- **基于数据决策**:修改策略必须基于评估反馈,不能凭空猜测
-- **引用具体评分**:在分析和决策时,引用具体的score数值和reason内容
-- **优先选择高分推荐词**:score >= 0.8 即可认为等价
-- **严禁编造数据**:
-  * 如果返回空列表,必须在 reason 中明确说明"未返回任何推荐词"
-  * 不能引用不存在的推荐词、评分或评估理由
-  * 每次 modify_query 的 reason 必须基于上一轮实际返回的结果
-""".strip()
-
-
-
-
-async def main():
-    current_time, log_url = set_trace()
-    q_context = read_file_as_string('input/kg_v1_single_context.md')
-    q = read_file_as_string('input/kg_v1_single_q.md')
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-    log_dir = os.path.join("logs", current_time)
-    run_context = RunContext(
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-    agent = Agent[RunContext](
-        name="Query Optimization Agent",
-        instructions=insrtuctions,
-        tools=[get_query_suggestions, modify_query],
-       
-    )
-    result = await Runner.run(agent, input=q_with_context,  context = run_context,)
-    print(result.final_output)
-
-    # 保存 RunContext 到 log_dir
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-
-if __name__ == "__main__":
-    asyncio.run(main())

+ 0 - 307
sug_v2_1_0.py

@@ -1,307 +0,0 @@
-import asyncio
-import json
-import os
-from datetime import datetime
-
-from agents import Agent, Runner, function_tool
-from lib.my_trace import set_trace
-from typing import Literal
-from dataclasses import dataclass
-from pydantic import BaseModel, Field
-
-
-from lib.utils import read_file_as_string
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-from agents import Agent, RunContextWrapper, Runner, function_tool
-
-from pydantic import BaseModel, Field
-
-
-class RunContext(BaseModel):
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-    # 中间数据记录 - 按时间顺序记录所有操作
-    operations_history: list[dict] = Field(default_factory=list, description="记录所有操作的历史,包括 get_query_suggestions 和 modify_query")
-
-
-eval_insrtuctions = """
-你是一个专业的评估专家,负责评估给定的搜索query是否满足原始问题和需求,给出出评分和简明扼要的理由。
-"""
-
-@dataclass
-class EvaluationFeedback:
-    reason: str=Field(..., description="简明扼要的理由")
-    score: float=Field(..., description="评估结果,1表示等价,0表示不等价,中间值表示部分等价")
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_insrtuctions,
-    output_type=EvaluationFeedback,
-)
-
-@function_tool
-async def get_query_suggestions(wrapper: RunContextWrapper[RunContext], query: str):
-    """Fetch search recommendations from Xiaohongshu."""
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-    query_suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-    print(query_suggestions)
-    async def evaluate_single_query(q_sug: str, q_with_context: str):
-        """Evaluate a single query suggestion."""
-        eval_input = f"""
-{q_with_context}
-<待评估的推荐query>
-{q_sug}
-</待评估的推荐query>
-        """
-        evaluator_result = await Runner.run(evaluator, eval_input)
-        result: EvaluationFeedback = evaluator_result.final_output
-        return {
-            "query": q_sug,
-            "score": result.score,
-            "reason": result.reason,
-        }
-
-    # 并发执行所有评估任务
-    q_with_context = wrapper.context.q_with_context
-    res = await asyncio.gather(*[evaluate_single_query(q_sug, q_with_context) for q_sug in query_suggestions])
-
-    # 记录到 RunContext
-    wrapper.context.operations_history.append({
-        "operation_type": "get_query_suggestions",
-        "timestamp": datetime.now().isoformat(),
-        "query": query,
-        "suggestions": query_suggestions,
-        "evaluations": res,
-    })
-
-    return res
-
-@function_tool
-def modify_query(wrapper: RunContextWrapper[RunContext], original_query: str, operation_type: str, new_query: str, reason: str):
-    """
-    Modify the search query with a specific operation.
-
-    Args:
-        original_query: The original query before modification
-        operation_type: Type of modification - must be one of:
-            - "简化": 删减query词,去除冗余词汇
-            - "扩展": 增加query语句,添加具体化的关键词
-            - "替换": 替换query中的某个词,使用同义词或专业术语
-            - "组合": 更换query词的顺序,调整为更符合用户习惯的词序
-        new_query: The modified query after applying the operation
-        reason: Detailed explanation of why this modification was made and what insight from previous suggestions led to this change.
-                Must reference specific data from get_query_suggestions results (e.g., scores, reasons, word patterns).
-
-    Returns:
-        A dict containing the modification record and the new query to use for next search
-    """
-    operation_types = ["简化", "扩展", "替换", "组合"]
-    if operation_type not in operation_types:
-        return {
-            "status": "error",
-            "message": f"Invalid operation_type. Must be one of: {', '.join(operation_types)}"
-        }
-
-    modification_record = {
-        "original_query": original_query,
-        "operation_type": operation_type,
-        "new_query": new_query,
-        "reason": reason,
-    }
-
-    # 记录到 RunContext
-    wrapper.context.operations_history.append({
-        "operation_type": "modify_query",
-        "timestamp": datetime.now().isoformat(),
-        "modification_type": operation_type,
-        "original_query": original_query,
-        "new_query": new_query,
-        "reason": reason,
-    })
-
-    return {
-        "status": "success",
-        "modification_record": modification_record,
-        "new_query": new_query,
-        "message": f"Query modified successfully. Use '{new_query}' for the next search."
-    }
-
-insrtuctions = """
-你是一个专业的搜索query优化专家,擅长通过动态探索找到最符合用户搜索习惯的query。
-
-## 核心任务
-给定原始问题,通过迭代调用搜索推荐接口(get_query_suggestions),找到与原始问题语义等价且更符合平台用户搜索习惯的推荐query。
-
-## 重要说明
-- **你不需要自己评估query的等价性**
-- get_query_suggestions 函数内部已集成评估子agent,会自动对每个推荐词进行评估
-- 返回结果包含:query(推荐词)、score(评分,1表示等价,0表示不等价)、reason(评估理由)
-- **你的职责是分析评估结果,做出决策和策略调整**
-
-## 防止幻觉 - 关键原则
-- **严禁编造数据**:只能基于 get_query_suggestions 实际返回的结果进行分析
-- **空结果处理**:如果返回的列表为空([]),必须明确说明"未返回任何推荐词"
-- **不要猜测**:在 modify_query 的 reason 中,不能引用不存在的推荐词或评分
-- **如实记录**:每次分析都要如实反映实际返回的数据
-
-## 工作流程
-
-### 1. 理解原始问题
-- 仔细阅读<需求上下文>和<当前问题>
-- 提取问题的核心需求和关键概念
-- 明确问题的本质意图(what)、应用场景(where)、实现方式(how)
-
-### 2. 动态探索策略
-
-**第一轮尝试:**
-- 使用原始问题直接调用 get_query_suggestions(query="原始问题")
-- **检查返回结果**:
-  - 如果返回空列表 []:说明"该query未返回任何推荐词",需要简化或替换query
-  - 如果有推荐词:查看每个推荐词的 score 和 reason
-- **做出判断**:是否有 score >= 0.8 的高分推荐词?
-
-**后续迭代:**
-如果没有高分推荐词(或返回空列表),必须先调用 modify_query 记录修改,然后再次搜索:
-
-**工具使用流程:**
-1. **分析评估反馈**(必须基于实际返回的数据):
-   - **情况A - 返回空列表**:
-     * 在 reason 中说明:"第X轮未返回任何推荐词,可能是query过于复杂或生僻"
-     * 不能编造任何推荐词或评分
-   - **情况B - 有推荐词但无高分**:
-     * 哪些推荐词得分较高?具体是多少分?评估理由是什么?
-     * 哪些推荐词偏离了原问题?如何偏离的?
-     * 推荐词整体趋势是什么?(过于泛化/具体化/领域偏移等)
-
-2. **决策修改策略**:基于实际评估反馈,调用 modify_query(original_query, operation_type, new_query, reason)
-   - reason 必须引用具体的数据,不能编造
-
-3. 使用返回的 new_query 调用 get_query_suggestions
-
-4. 分析新的评估结果,如果仍不满足,重复步骤1-3
-
-**四种操作类型(operation_type)及其详细判定标准:**
-
-**策略1:扩展(增加 query 语句)**
-- **判定标准**:
-  * 有效sug列表中,sug_i 的语义比 Query 更具体,且词汇数量 > query 的词汇数量
-  * sug list 中的每个词是 Query 的更具体或细化版本,是能将 Query 具体化描述的关键新词
-  * sug_i 必须包含 Query 的大部分或所有核心关键词,且词汇数量都明显多于query
-  * sug_j 中存在 query 中不存在的关键新词集合(例如:特定平台名称、特定功能词)
-- **使用时机**:当推荐词整体趋势是添加更具体的限定词或场景描述时
-
-**策略2:简化(删减 query 词)**
-- **判定标准**(满足以下任一条件即可):
-  * 有效sug列表中,大部分 sug 的词汇数量都明显少于 query
-  * 当sug列表为空时
-  * sug 词汇数量明显少于 query,且与 Query 的核心搜索意图一致
-  * 基于评估反馈,识别 query 中 1-2 个词为"非核心冗余词",且删除后不影响核心搜索意图
-- **使用时机**:当原query过于冗长复杂,推荐词都倾向于使用更简洁的表达时
-
-**策略3:组合(更换 query 词的顺序)**
-- **判定标准**:
-  * 有效sug list中,存在多个 sug_i 与 query 拥有完全相同的核心关键词集合,只是词汇排列顺序不同
-  * sug_i 与 query 的词序不同,但 sug_i 的词序变化与 query 的核心语义一致
-  * sug_i 的词序更自然或更符合搜索习惯,且 sug_i 的词序频率高于 query 词的词序
-  * 多个 Sug_List 中的 sug_i 都倾向于使用与 Query 不同但意思一致的词序
-- **使用时机**:当推荐词与原query关键词相同但顺序不同,且新顺序更符合用户习惯时
-
-**策略4:替换(替换 query 语句中的某个词)**
-- **判定标准**:
-  * 存在 sug_i(来自有效suglist),其与 query 之间仅有 1-2 个核心词不同,但其他词均相同
-  * sug_i 作为整体查询词,其语义与 query 等价或更优
-  * sug_i 与 Query 在大部分词汇上重叠,但在 1-2 个关键位置使用了不同的词
-  * 这些不同的词在语义上是同义、近义或在相关领域中更流行/专业的替代词
-  * 差异词中的新词在 Platform Sug List 中出现频率更高,或被标记为更专业的术语
-- **使用时机**:当推荐词显示某个词的同义词或专业术语更受欢迎时
-
-**每次修改的reason必须包含:**
-- 上一轮评估结果的关键发现(引用具体的score和reason)
-- 基于评估反馈,为什么这样修改
-- 预期这次修改会带来什么改进
-
-### 3. 决策标准
-- **score >= 0.8**:认为该推荐词与原问题等价,可以作为最终结果
-- **0.5 <= score < 0.8**:部分等价,分析reason看是否可接受
-- **score < 0.5**:不等价,需要继续优化
-
-### 4. 迭代终止条件
-- **成功终止**:找到至少一个 score >= 0.8 的推荐query
-- **尝试上限**:最多迭代5轮,避免无限循环
-- **无推荐词**:推荐接口返回空列表或错误
-
-### 5. 输出要求
-
-**成功找到等价query时,输出格式:**
-```
-原始问题:[原问题]
-优化后的query:[最终找到的等价推荐query]
-评分:[score]
-```
-
-**未找到等价query时,输出格式:**
-```
-原始问题:[原问题]
-结果:未找到完全等价的推荐query
-建议:[简要建议,如:直接使用原问题搜索 或 使用最接近的推荐词]
-```
-
-## 注意事项
-- **第一轮必须使用原始问题**:直接调用 get_query_suggestions(query="原始问题")
-- **后续修改必须调用 modify_query**:不能直接用新query调用 get_query_suggestions
-- **重点关注评估结果**:每次都要仔细分析返回的 score 和 reason
-- **基于数据决策**:修改策略必须基于评估反馈,不能凭空猜测
-- **引用具体评分**:在分析和决策时,引用具体的score数值和reason内容
-- **优先选择高分推荐词**:score >= 0.8 即可认为等价
-- **严禁编造数据**:
-  * 如果返回空列表,必须在 reason 中明确说明"未返回任何推荐词"
-  * 不能引用不存在的推荐词、评分或评估理由
-  * 每次 modify_query 的 reason 必须基于上一轮实际返回的结果
-""".strip()
-
-
-
-
-async def main():
-    current_time, log_url = set_trace()
-    q_context = read_file_as_string('input/kg_v1_single_context.md')
-    q = read_file_as_string('input/kg_v1_single_q.md')
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-    log_dir = os.path.join("logs", current_time)
-    run_context = RunContext(
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-    agent = Agent[RunContext](
-        name="Query Optimization Agent",
-        instructions=insrtuctions,
-        tools=[get_query_suggestions, modify_query],
-       
-    )
-    result = await Runner.run(agent, input=q_with_context,  context = run_context,)
-    print(result.final_output)
-
-    # 保存 RunContext 到 log_dir
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-
-if __name__ == "__main__":
-    asyncio.run(main())

+ 0 - 301
sug_v2_raw_v1.py

@@ -1,301 +0,0 @@
-import asyncio
-import json
-import os
-import argparse
-from datetime import datetime
-
-from agents import Agent, Runner, function_tool
-from lib.my_trace import set_trace
-from typing import Literal
-from dataclasses import dataclass
-from pydantic import BaseModel, Field
-
-
-from lib.utils import read_file_as_string
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-from agents import Agent, RunContextWrapper, Runner, function_tool
-
-from pydantic import BaseModel, Field
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射,如 {'context_file': '...', 'q_file': '...'}")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-    # 中间数据记录 - 按时间顺序记录所有操作
-    operations_history: list[dict] = Field(default_factory=list, description="记录所有操作的历史,包括 get_query_suggestions 和 modify_query")
-    # 最终输出结果
-    final_output: str | None = Field(default=None, description="Agent的最终输出结果")
-
-
-eval_insrtuctions = """
-你是一个专业的评估专家,负责评估给定的搜索query是否满足原始问题和需求,给出出评分和简明扼要的理由。
-"""
-
-@dataclass
-class EvaluationFeedback:
-    reason: str=Field(..., description="简明扼要的理由")
-    score: float=Field(..., description="评估结果,1表示等价,0表示不等价,中间值表示部分等价")
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_insrtuctions,
-    output_type=EvaluationFeedback,
-)
-
-@function_tool
-async def get_query_suggestions(wrapper: RunContextWrapper[RunContext], query: str):
-    """Fetch search recommendations from Xiaohongshu."""
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-    query_suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-     # 记录到 RunContext
-    wrapper.context.operations_history.append({
-        "operation_type": "get_query_suggestions",
-        "timestamp": datetime.now().isoformat(),
-        "query": query,
-        "suggestions": query_suggestions,
-    })
-
-    if query_suggestions:
-        return query_suggestions
-    else:
-        res = '未返回任何推荐词'
-
-   
-    return res
-
-@function_tool
-def modify_query(wrapper: RunContextWrapper[RunContext], original_query: str, operation_type: str, new_query: str, reason: str):
-    """
-    Modify the search query with a specific operation.
-
-    Args:
-        original_query: The original query before modification
-        operation_type: Type of modification - must be one of: "简化", "扩展", "替换", "组合"
-        new_query: The modified query after applying the operation
-        reason: Detailed explanation of why this modification was made and what insight from previous suggestions led to this change
-
-    Returns:
-        A dict containing the modification record and the new query to use for next search
-    """
-    operation_types = ["简化", "扩展", "替换", "组合"]
-    if operation_type not in operation_types:
-        return {
-            "status": "error",
-            "message": f"Invalid operation_type. Must be one of: {', '.join(operation_types)}"
-        }
-
-    modification_record = {
-        "original_query": original_query,
-        "operation_type": operation_type,
-        "new_query": new_query,
-        "reason": reason,
-    }
-
-    # 记录到 RunContext
-    wrapper.context.operations_history.append({
-        "operation_type": "modify_query",
-        "timestamp": datetime.now().isoformat(),
-        "modification_type": operation_type,
-        "original_query": original_query,
-        "new_query": new_query,
-        "reason": reason,
-    })
-
-    return {
-        "status": "success",
-        "modification_record": modification_record,
-        "new_query": new_query,
-        "message": f"Query modified successfully. Use '{new_query}' for the next search."
-    }
-
-insrtuctions = """
-你是一个专业的搜索query优化专家,擅长通过动态探索找到最符合用户搜索习惯的query。
-
-## 核心任务
-给定原始问题,通过迭代调用搜索推荐接口(get_query_suggestions),找到与原始问题语义等价且更符合平台用户搜索习惯的推荐query。
-
-## 工作流程
-
-### 1. 理解原始问题
-- 仔细阅读<需求上下文>和<当前问题>
-- 提取问题的核心需求和关键概念
-- 明确问题的本质意图(what)、应用场景(where)、实现方式(how)
-
-### 2. 动态探索策略
-采用类似人类搜索的迭代探索方式:
-
-**第一轮尝试:**
-- 使用原始问题直接调用 get_query_suggestions(query="原始问题")
-- 仔细分析返回的推荐词列表
-- 判断是否有与原始问题等价的推荐词
-
-**后续迭代:**
-如果推荐词不满足要求,必须先调用 modify_query 函数记录修改,然后再次搜索:
-
-**工具使用流程:**
-1. 调用 modify_query(original_query, operation_type, new_query, reason)
-2. 使用返回的 new_query 调用 get_query_suggestions
-3. 分析新的推荐词列表
-4. 如果仍不满足,重复步骤1-3
-
-**四种操作类型(operation_type):**
-- **简化**:删除冗余词汇,提取核心关键词
-- **扩展**
-- **替换**:使用同义词、行业术语或口语化表达
-- **组合**:调整关键词顺序或组合方式
-
-**每次修改的reason必须包含:**
-- 上一轮推荐词给你的启发
-- 为什么这样修改更符合平台用户习惯
-- 与原始问题的关系(确保核心意图不变)
-
-### 3. 等价性判断标准
-推荐词满足以下条件即可视为"与原始问题等价":
-
-**语义等价:**
-- 能够回答或解决原始问题的核心需求
-- 涵盖原始问题的关键功能或场景
-
-
-### 4. 迭代终止条件
-- **成功终止**:找到至少一个与原始问题等价的推荐query
-- **尝试上限**:最多迭代5轮,避免无限循环
-- **无推荐词**:推荐接口返回空列表或错误
-
-### 5. 输出要求
-成功找到等价query时,输出格式:
-```
-原始问题:[原问题]
-
-优化后的query:[最终找到的等价推荐query]
-
-探索路径:
-1. 第1轮:原始query "[query1]"
-   - 推荐词:[列出关键推荐词]
-   - 判断:不满足,[简要说明原因]
-
-2. 第2轮:modify_query("[query1]", "简化", "[query2]", "[reason]")
-   - 推荐词:[列出关键推荐词]
-   - 判断:不满足,[简要说明原因]
-
-3. 第3轮:modify_query("[query2]", "替换", "[query3]", "[reason]")
-   - 推荐词:[列出关键推荐词]
-   - 判断:满足!找到等价query "[最终query]"
-
-推荐理由:
-- 该query来自平台官方推荐,大概率有结果
-- 与原始问题语义等价:[具体说明]
-- 更符合用户搜索习惯:[具体说明]
-```
-
-未找到等价query时,输出:
-```
-原始问题:[原问题]
-
-探索结果:未找到完全等价的推荐query(已尝试[N]轮)
-
-尝试过的query及修改记录:
-1. "[query1]" (原始)
-2. "[query2]" (简化:[reason])
-3. "[query3]" (替换:[reason])
-...
-
-各轮推荐词分析:
-- 第1轮推荐词偏向:[分析]
-- 第2轮推荐词偏向:[分析]
-...
-
-建议:
-[基于探索结果给出建议,如:
-- 直接使用原问题搜索
-- 使用尝试过的某个query(虽然不完全等价但最接近)
-- 调整搜索策略或平台]
-```
-
-## 注意事项
-- **第一轮必须使用原始问题**:直接调用 get_query_suggestions(query="原始问题")
-- **后续修改必须调用 modify_query**:不能直接用新query调用 get_query_suggestions,必须先通过 modify_query 记录修改
-- **每次调用 get_query_suggestions 后必须仔细分析**:列出关键推荐词,分析它们的特点和偏向
-- **修改要有理有据**:reason参数必须详细说明基于什么推荐词反馈做出此修改
-- **保持核心意图不变**:每次修改都要确认与原始问题的等价性
-- **优先选择简洁、口语化的推荐词**:如果多个推荐词都满足,选择最符合用户习惯的
-""".strip()
-
-
-
-
-async def main(input_dir: str):
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]  # 去掉 .py 后缀
-
-    # 日志保存到输入目录的 output/版本/时间戳 目录下
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-    agent = Agent[RunContext](
-        name="Query Optimization Agent",
-        instructions=insrtuctions,
-        tools=[get_query_suggestions, modify_query],
-       
-    )
-    result = await Runner.run(agent, input=q_with_context,  context = run_context,)
-    print(result.final_output)
-
-    # 保存最终输出到 RunContext
-    run_context.final_output = str(result.final_output)
-
-    # 保存 RunContext 到 log_dir
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    args = parser.parse_args()
-
-    asyncio.run(main(args.input_dir))

+ 0 - 291
sug_v2_raw_v2.py

@@ -1,291 +0,0 @@
-import asyncio
-import json
-import os
-import argparse
-from datetime import datetime
-
-from agents import Agent, Runner, function_tool
-from lib.my_trace import set_trace
-from typing import Literal
-from dataclasses import dataclass
-from pydantic import BaseModel, Field
-
-
-from lib.utils import read_file_as_string
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-from agents import Agent, RunContextWrapper, Runner, function_tool
-
-from pydantic import BaseModel, Field
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射,如 {'context_file': '...', 'q_file': '...'}")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-    # 中间数据记录 - 按时间顺序记录所有操作
-    operations_history: list[dict] = Field(default_factory=list, description="记录所有操作的历史,包括 get_query_suggestions 和 modify_query")
-    # 最终输出结果
-    final_output: str | None = Field(default=None, description="Agent的最终输出结果")
-
-
-eval_insrtuctions = """
-你是一个专业的评估专家,负责评估给定的搜索query是否满足原始问题和需求,给出出评分和简明扼要的理由。
-"""
-
-@dataclass
-class EvaluationFeedback:
-    reason: str=Field(..., description="简明扼要的理由")
-    score: float=Field(..., description="评估结果,1表示等价,0表示不等价,中间值表示部分等价")
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_insrtuctions,
-    output_type=EvaluationFeedback,
-)
-
-@function_tool
-async def get_query_suggestions(wrapper: RunContextWrapper[RunContext], query: str):
-    """Fetch search recommendations from Xiaohongshu."""
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-    query_suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-    # 记录到 RunContext
-    wrapper.context.operations_history.append({
-        "operation_type": "get_query_suggestions",
-        "timestamp": datetime.now().isoformat(),
-        "query": query,
-        "suggestions": query_suggestions,
-    })
-
-    if query_suggestions:
-        return query_suggestions
-    else:
-        res = '未返回任何推荐词'
-
-    return res
-
-@function_tool
-def modify_query(wrapper: RunContextWrapper[RunContext], original_query: str, operation_type: str, new_query: str, reason: str):
-    """
-    Modify the search query with a specific operation.
-
-    Args:
-        original_query: The original query before modification
-        operation_type: Type of modification - must be one of: "简化", "扩展", "替换", "组合"
-        new_query: The modified query after applying the operation
-        reason: Detailed explanation of why this modification was made and what insight from previous suggestions led to this change
-
-    Returns:
-        A dict containing the modification record and the new query to use for next search
-    """
-    operation_types = ["简化", "扩展", "替换", "组合"]
-    if operation_type not in operation_types:
-        return {
-            "status": "error",
-            "message": f"Invalid operation_type. Must be one of: {', '.join(operation_types)}"
-        }
-
-    modification_record = {
-        "original_query": original_query,
-        "operation_type": operation_type,
-        "new_query": new_query,
-        "reason": reason,
-    }
-
-    # 记录到 RunContext
-    wrapper.context.operations_history.append({
-        "operation_type": "modify_query",
-        "timestamp": datetime.now().isoformat(),
-        "modification_type": operation_type,
-        "original_query": original_query,
-        "new_query": new_query,
-        "reason": reason,
-    })
-
-    return {
-        "status": "success",
-        "modification_record": modification_record,
-        "new_query": new_query,
-        "message": f"Query modified successfully. Use '{new_query}' for the next search."
-    }
-
-insrtuctions = """
-你是一个专业的搜索query优化专家,擅长通过动态探索找到最符合用户搜索习惯的query。
-
-## 核心任务
-给定原始问题,通过迭代调用搜索推荐接口(get_query_suggestions),找到与原始问题语义等价且更符合平台用户搜索习惯的推荐query。
-
-## 重要说明
-- **你不需要自己评估query的等价性**
-- get_query_suggestions 函数内部已集成评估子agent,会自动对每个推荐词进行评估
-- 返回结果包含:query(推荐词)、score(评分,1表示等价,0表示不等价)、reason(评估理由)
-- **你的职责是分析评估结果,做出决策和策略调整**
-
-## 防止幻觉 - 关键原则
-- **严禁编造数据**:只能基于 get_query_suggestions 实际返回的结果进行分析
-- **空结果处理**:如果返回的列表为空([]),必须明确说明"未返回任何推荐词"
-- **不要猜测**:在 modify_query 的 reason 中,不能引用不存在的推荐词或评分
-- **如实记录**:每次分析都要如实反映实际返回的数据
-
-## 工作流程
-
-### 1. 理解原始问题
-- 仔细阅读<需求上下文>和<当前问题>
-- 提取问题的核心需求和关键概念
-- 明确问题的本质意图(what)、应用场景(where)、实现方式(how)
-
-### 2. 动态探索策略
-
-**第一轮尝试:**
-- 使用原始问题直接调用 get_query_suggestions(query="原始问题")
-- **检查返回结果**:
-  - 如果返回空列表 []:说明"该query未返回任何推荐词",需要简化或替换query
-  - 如果有推荐词:查看每个推荐词的 score 和 reason
-- **做出判断**:是否有 score >= 0.8 的高分推荐词?
-
-**后续迭代:**
-如果没有高分推荐词(或返回空列表),必须先调用 modify_query 记录修改,然后再次搜索:
-
-**工具使用流程:**
-1. **分析评估反馈**(必须基于实际返回的数据):
-   - **情况A - 返回空列表**:
-     * 在 reason 中说明:"第X轮未返回任何推荐词,可能是query过于复杂或生僻"
-     * 不能编造任何推荐词或评分
-   - **情况B - 有推荐词但无高分**:
-     * 哪些推荐词得分较高?具体是多少分?评估理由是什么?
-     * 哪些推荐词偏离了原问题?如何偏离的?
-     * 推荐词整体趋势是什么?(过于泛化/具体化/领域偏移等)
-
-2. **决策修改策略**:基于实际评估反馈,调用 modify_query(original_query, operation_type, new_query, reason)
-   - reason 必须引用具体的数据,不能编造
-
-3. 使用返回的 new_query 调用 get_query_suggestions
-
-4. 分析新的评估结果,如果仍不满足,重复步骤1-3
-
-**四种操作类型(operation_type):**
-- **简化**:删除冗余词汇,提取核心关键词(当推荐词过于发散时)
-- **扩展**:添加限定词或场景描述(当推荐词过于泛化时)
-- **替换**:使用同义词、行业术语或口语化表达(当推荐词偏离核心时)
-- **组合**:调整关键词顺序或组合方式(当推荐词结构不合理时)
-
-**每次修改的reason必须包含:**
-- 上一轮评估结果的关键发现(引用具体的score和reason)
-- 基于评估反馈,为什么这样修改
-- 预期这次修改会带来什么改进
-
-### 3. 决策标准
-- **score >= 0.8**:认为该推荐词与原问题等价,可以作为最终结果
-- **0.5 <= score < 0.8**:部分等价,分析reason看是否可接受
-- **score < 0.5**:不等价,需要继续优化
-
-### 4. 迭代终止条件
-- **成功终止**:找到至少一个 score >= 0.8 的推荐query
-- **尝试上限**:最多迭代5轮,避免无限循环
-- **无推荐词**:推荐接口返回空列表或错误
-
-### 5. 输出要求
-
-**成功找到等价query时,输出格式:**
-```
-原始问题:[原问题]
-优化后的query:[最终找到的等价推荐query]
-评分:[score]
-```
-
-**未找到等价query时,输出格式:**
-```
-原始问题:[原问题]
-结果:未找到完全等价的推荐query
-建议:[简要建议,如:直接使用原问题搜索 或 使用最接近的推荐词]
-```
-
-## 注意事项
-- **第一轮必须使用原始问题**:直接调用 get_query_suggestions(query="原始问题")
-- **后续修改必须调用 modify_query**:不能直接用新query调用 get_query_suggestions
-- **重点关注评估结果**:每次都要仔细分析返回的 score 和 reason
-- **基于数据决策**:修改策略必须基于评估反馈,不能凭空猜测
-- **引用具体评分**:在分析和决策时,引用具体的score数值和reason内容
-- **优先选择高分推荐词**:score >= 0.8 即可认为等价
-- **严禁编造数据**:
-  * 如果返回空列表,必须在 reason 中明确说明"未返回任何推荐词"
-  * 不能引用不存在的推荐词、评分或评估理由
-  * 每次 modify_query 的 reason 必须基于上一轮实际返回的结果
-""".strip()
-
-
-
-
-async def main(input_dir: str):
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]  # 去掉 .py 后缀
-
-    # 日志保存到输入目录的 output/版本/时间戳 目录下
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-    agent = Agent[RunContext](
-        name="Query Optimization Agent",
-        instructions=insrtuctions,
-        tools=[get_query_suggestions, modify_query],
-       
-    )
-    result = await Runner.run(agent, input=q_with_context,  context = run_context,)
-    print(result.final_output)
-
-    # 保存最终输出到 RunContext
-    run_context.final_output = str(result.final_output)
-
-    # 保存 RunContext 到 log_dir
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    args = parser.parse_args()
-
-    asyncio.run(main(args.input_dir))

+ 0 - 383
sug_v2_with_eval_requery_yx.py

@@ -1,383 +0,0 @@
-import asyncio
-import json
-import os
-import argparse
-from datetime import datetime
-
-from agents import Agent, Runner, function_tool
-from lib.my_trace import set_trace
-from typing import Literal
-from dataclasses import dataclass
-from pydantic import BaseModel, Field
-
-
-from lib.utils import read_file_as_string
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-from agents import Agent, RunContextWrapper, Runner, function_tool
-
-from pydantic import BaseModel, Field
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射,如 {'context_file': '...', 'q_file': '...'}")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-    # 中间数据记录 - 按时间顺序记录所有操作
-    operations_history: list[dict] = Field(default_factory=list, description="记录所有操作的历史,包括 get_query_suggestions 和 modify_query")
-    # 最终输出结果
-    final_output: str | None = Field(default=None, description="Agent的最终输出结果")
-
-
-eval_insrtuctions = """
-# 角色定义
-
-你是一个 **专业的语言专家和语义相关性评判专家**。
-
-你的任务是:判断我给你的平台sug词条,其与 <原始 query 问题> 和 <需求上下文> **共同形成的综合意图** 的相关度满足度百分比。
-
----
-
-# 输入信息
-
-你将接收到以下输入:
-1.  <需求上下文>:文档的解构结果或语义说明,用于理解 query 所针对的核心任务与场景。
-2.  <原始 query 问题>:用户的初始查询问题。
-3.  <平台sug词条>:模型或算法推荐的 平台sug词条,其中包含待评估的词条。
-
----
-
-# 工作流程
-
-## 第一步:理解综合意图
-
--   仔细阅读 <需求上下文> 与 <原始 query 问题>;
--   明确 <原始 query 问题> 的核心意图、主题焦点和任务目标;
--   提炼 <需求上下文> 所描述的核心任务、场景、概念和语义范围;
--   **将 <原始 query 问题> 和 <需求上下文> 融合成一个单一的、完整的“综合意图”**,作为后续评估的唯一基准。这个综合意图代表了用户在特定场景下最核心、最全面的需求。
-
-## 第二步:逐一评估 Sug 词条的综合相关度满足度
-
-对于 我给你的sug词条:
-
-1.  **评估其与“综合意图”的相关度满足度:**
-    -   将当前 sug 词条的语义与第一步中确定的“综合意图”(<原始 query 问题> 和 <需求上下文> 的结合体)进行比较。
-    -   全面评估该 sug 词条在主题、目标动作、语义范围、场景匹配、核心概念覆盖等各方面与“综合意图”的匹配和满足程度。
-    -   给出 0-1 之间的浮点数,代表其“相关度满足度”。数值比越高,表示该 sug 词条越能充分且精准地满足“综合意图”。
-
-**评估标准一致性要求:在评估 sug 词条时,请务必保持你对“相关度满足度”的判断标准和百分比给定的逻辑完全一致和稳定。**
-
-**相关性依据要求:** 简短叙述相关性依据,说明主要匹配点或差异点,以及该 sug 词条如何满足或未能完全满足“综合意图”的哪些方面。
-"""
-
-@dataclass
-class EvaluationFeedback:
-    reason: str=Field(..., description="简明扼要的理由")
-    score: float=Field(..., description="评估结果,1表示等价,0表示不等价,中间值表示部分等价")
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_insrtuctions,
-    output_type=EvaluationFeedback,
-)
-
-@function_tool
-async def get_query_suggestions(wrapper: RunContextWrapper[RunContext], query: str):
-    """Fetch search recommendations from Xiaohongshu."""
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-    query_suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-    print(query_suggestions)
-    async def evaluate_single_query(q_sug: str, q_with_context: str):
-        """Evaluate a single query suggestion."""
-        eval_input = f"""
-{q_with_context}
-<待评估的推荐query>
-{q_sug}
-</待评估的推荐query>
-        """
-        evaluator_result = await Runner.run(evaluator, eval_input)
-        result: EvaluationFeedback = evaluator_result.final_output
-        return {
-            "query": q_sug,
-            "score": result.score,
-            "reason": result.reason,
-        }
-
-    # 并发执行所有评估任务
-    q_with_context = wrapper.context.q_with_context
-    res = []
-    if query_suggestions:
-        res = await asyncio.gather(*[evaluate_single_query(q_sug, q_with_context) for q_sug in query_suggestions])
-    else:
-        res = '未返回任何推荐词'
-
-    # 记录到 RunContext
-    wrapper.context.operations_history.append({
-        "operation_type": "get_query_suggestions",
-        "timestamp": datetime.now().isoformat(),
-        "query": query,
-        "suggestions": query_suggestions,
-        "evaluations": res,
-    })
-
-    return res
-
-@function_tool
-def modify_query(wrapper: RunContextWrapper[RunContext], original_query: str, operation_type: str, new_query: str, reason: str):
-    """
-    Modify the search query with a specific operation.
-
-    Args:
-        original_query: The original query before modification
-        operation_type: Type of modification - must be one of:
-            - "增加": 增加query语句,添加具体化的关键词
-            - "删减": 删减query词,去除冗余词汇
-            - "调序": 调整query词的顺序,更符合用户习惯的词序
-            - "替换": 替换query中的某个词,使用同义词或专业术语
-        new_query: The modified query after applying the operation
-        reason: Detailed explanation of why this modification was made and what insight from previous suggestions led to this change.
-                Must reference specific data from get_query_suggestions results (e.g., scores, reasons, word patterns).
-
-    Returns:
-        A dict containing the modification record and the new query to use for next search
-    """
-    operation_types = ["增加", "删减", "调序", "替换"]
-    if operation_type not in operation_types:
-        return {
-            "status": "error",
-            "message": f"Invalid operation_type. Must be one of: {', '.join(operation_types)}"
-        }
-
-    modification_record = {
-        "original_query": original_query,
-        "operation_type": operation_type,
-        "new_query": new_query,
-        "reason": reason,
-    }
-
-    # 记录到 RunContext
-    wrapper.context.operations_history.append({
-        "operation_type": "modify_query",
-        "timestamp": datetime.now().isoformat(),
-        "modification_type": operation_type,
-        "original_query": original_query,
-        "new_query": new_query,
-        "reason": reason,
-    })
-
-    return {
-        "status": "success",
-        "modification_record": modification_record,
-        "new_query": new_query,
-        "message": f"Query modified successfully. Use '{new_query}' for the next search."
-    }
-
-insrtuctions = """
-你是一个专业的搜索query优化专家,擅长通过动态探索找到最符合用户搜索习惯的query。
-
-## 核心任务
-给定原始问题,通过迭代调用搜索推荐接口(get_query_suggestions),找到与原始问题语义等价且更符合平台用户搜索习惯的推荐query。
-
-## 重要说明
-- **你不需要自己评估query的等价性**
-- get_query_suggestions 函数内部已集成评估子agent,会自动对每个推荐词进行评估
-- 返回结果包含:query(推荐词)、score(评分,1表示等价,0表示不等价)、reason(评估理由)
-- **你的职责是分析评估结果,做出决策和策略调整**
-
-## 防止幻觉 - 关键原则
-- **严禁编造数据**:只能基于 get_query_suggestions 实际返回的结果进行分析
-- **空结果处理**:如果返回的列表为空([]),必须明确说明"未返回任何推荐词"
-- **不要猜测**:在 modify_query 的 reason 中,不能引用不存在的推荐词或评分
-- **如实记录**:每次分析都要如实反映实际返回的数据
-
-## 工作流程
-
-### 1. 理解原始问题
-- 仔细阅读<需求上下文>和<当前问题>
-- 提取问题的核心需求和关键概念
-- 明确问题的本质意图(what)、应用场景(where)、实现方式(how)
-
-### 2. 动态探索策略
-
-**第一轮尝试:**
-- 使用原始问题直接调用 get_query_suggestions(query="原始问题")
-- **检查返回结果**:
-  - 如果返回空列表 []:说明"该query未返回任何推荐词",需要简化或替换query
-  - 如果有推荐词:查看每个推荐词的 score 和 reason
-- **做出判断**:是否有 score >= 0.8 的高分推荐词?
-
-**后续迭代:**
-如果没有高分推荐词(或返回空列表),必须先调用 modify_query 记录修改,然后再次搜索:
-
-**工具使用流程:**
-1. **分析评估反馈**(必须基于实际返回的数据):
-   - **情况A - 返回空列表**:
-     * 在 reason 中说明:"第X轮未返回任何推荐词,可能是query过于复杂或生僻"
-     * 不能编造任何推荐词或评分
-   - **情况B - 有推荐词但无高分**:
-     * 哪些推荐词得分较高?具体是多少分?评估理由是什么?
-     * 哪些推荐词偏离了原问题?如何偏离的?
-     * 推荐词整体趋势是什么?(过于泛化/具体化/领域偏移等)
-
-2. **决策修改策略**:基于实际评估反馈,调用 modify_query(original_query, operation_type, new_query, reason)
-   - reason 必须引用具体的数据,不能编造
-
-3. 使用返回的 new_query 调用 get_query_suggestions
-
-4. 分析新的评估结果,如果仍不满足,重复步骤1-3
-
-**四种操作类型(operation_type)及其详细判定标准:**
-
-**策略1:增加**
-- **判定标准**:
-  * 有效sug列表中,sug_i 的语义比 Query 更具体,且词汇数量 > query 的词汇数量
-  * sug list 中的每个词是 Query 的更具体或细化版本,是能将 Query 具体化描述的关键新词
-  * sug_i 必须包含 Query 的大部分或所有核心关键词,且词汇数量都明显多于query
-  * sug_j 中存在 query 中不存在的关键新词集合(例如:特定平台名称、特定功能词)
-- **使用时机**:当推荐词整体趋势是添加更具体的限定词或场景描述时
-
-**策略2:删减**
-- **判定标准**(满足以下任一条件即可):
-  * 有效sug列表中,大部分 sug 的词汇数量都明显少于 query
-  * 当sug列表为空时
-  * sug 词汇数量明显少于 query,且与 Query 的核心搜索意图一致
-  * 基于评估反馈,识别 query 中 1-2 个词为"非核心冗余词",且删除后不影响核心搜索意图
-- **使用时机**:当原query过于冗长复杂,推荐词都倾向于使用更简洁的表达时
-
-**策略3:调序**
-- **判定标准**:
-  * 有效sug list中,存在多个 sug_i 与 query 拥有完全相同的核心关键词集合,只是词汇排列顺序不同
-  * sug_i 与 query 的词序不同,但 sug_i 的词序变化与 query 的核心语义一致
-  * sug_i 的词序更自然或更符合搜索习惯,且 sug_i 的词序频率高于 query 词的词序
-  * 多个 Sug_List 中的 sug_i 都倾向于使用与 Query 不同但意思一致的词序
-- **使用时机**:当推荐词与原query关键词相同但顺序不同,且新顺序更符合用户习惯时
-
-**策略4:替换**
-- **判定标准**:
-  * 存在 sug_i(来自有效suglist),其与 query 之间仅有 1-2 个核心词不同,但其他词均相同
-  * sug_i 作为整体查询词,其语义与 query 等价或更优
-  * sug_i 与 Query 在大部分词汇上重叠,但在 1-2 个关键位置使用了不同的词
-  * 这些不同的词在语义上是同义、近义或在相关领域中更流行/专业的替代词
-  * 差异词中的新词在 Platform Sug List 中出现频率更高,或被标记为更专业的术语
-- **使用时机**:当推荐词显示某个词的同义词或专业术语更受欢迎时
-
-**每次修改的reason必须包含:**
-- 上一轮评估结果的关键发现(引用具体的score和reason)
-- 基于评估反馈,为什么这样修改
-- 预期这次修改会带来什么改进
-
-### 3. 决策标准
-- **score >= 0.8**:认为该推荐词与原问题等价,可以作为最终结果
-- **0.5 <= score < 0.8**:部分等价,分析reason看是否可接受
-- **score < 0.5**:不等价,需要继续优化
-
-### 4. 迭代终止条件
-- **成功终止**:找到至少一个 score >= 0.8 的推荐query
-- **尝试上限**:最多迭代5轮,避免无限循环
-- **无推荐词**:推荐接口返回空列表或错误
-
-### 5. 输出要求
-
-**成功找到等价query时,输出格式:**
-```
-原始问题:[原问题]
-优化后的query:[最终找到的等价推荐query]
-评分:[score]
-```
-
-**未找到等价query时,输出格式:**
-```
-原始问题:[原问题]
-结果:未找到完全等价的推荐query
-建议:[简要建议,如:直接使用原问题搜索 或 使用最接近的推荐词]
-```
-
-## 注意事项
-- **第一轮必须使用原始问题**:直接调用 get_query_suggestions(query="原始问题")
-- **后续修改必须调用 modify_query**:不能直接用新query调用 get_query_suggestions
-- **重点关注评估结果**:每次都要仔细分析返回的 score 和 reason
-- **基于数据决策**:修改策略必须基于评估反馈,不能凭空猜测
-- **引用具体评分**:在分析和决策时,引用具体的score数值和reason内容
-- **优先选择高分推荐词**:score >= 0.8 即可认为等价
-- **严禁编造数据**:
-  * 如果返回空列表,必须在 reason 中明确说明"未返回任何推荐词"
-  * 不能引用不存在的推荐词、评分或评估理由
-  * 每次 modify_query 的 reason 必须基于上一轮实际返回的结果
-""".strip()
-
-
-
-
-async def main(input_dir: str):
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]  # 去掉 .py 后缀
-
-    # 日志保存到输入目录的 output/版本/时间戳 目录下
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-    agent = Agent[RunContext](
-        name="Query Optimization Agent",
-        instructions=insrtuctions,
-        tools=[get_query_suggestions, modify_query],
-       
-    )
-    result = await Runner.run(agent, input=q_with_context,  context = run_context,)
-    print(result.final_output)
-
-    # 保存最终输出到 RunContext
-    run_context.final_output = str(result.final_output)
-
-    # 保存 RunContext 到 log_dir
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    args = parser.parse_args()
-
-    asyncio.run(main(args.input_dir))

+ 0 - 349
sug_v2_with_eval_yx.py

@@ -1,349 +0,0 @@
-import asyncio
-import json
-import os
-import argparse
-from datetime import datetime
-
-from agents import Agent, Runner, function_tool
-from lib.my_trace import set_trace
-from typing import Literal
-from dataclasses import dataclass
-from pydantic import BaseModel, Field
-
-
-from lib.utils import read_file_as_string
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-from agents import Agent, RunContextWrapper, Runner, function_tool
-
-from pydantic import BaseModel, Field
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射,如 {'context_file': '...', 'q_file': '...'}")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-    # 中间数据记录 - 按时间顺序记录所有操作
-    operations_history: list[dict] = Field(default_factory=list, description="记录所有操作的历史,包括 get_query_suggestions 和 modify_query")
-    # 最终输出结果
-    final_output: str | None = Field(default=None, description="Agent的最终输出结果")
-
-
-eval_insrtuctions = """
-# 角色定义
-
-你是一个 **专业的语言专家和语义相关性评判专家**。
-
-你的任务是:判断我给你的平台sug词条,其与 <原始 query 问题> 和 <需求上下文> **共同形成的综合意图** 的相关度满足度百分比。
-
----
-
-# 输入信息
-
-你将接收到以下输入:
-1.  <需求上下文>:文档的解构结果或语义说明,用于理解 query 所针对的核心任务与场景。
-2.  <原始 query 问题>:用户的初始查询问题。
-3.  <平台sug词条>:模型或算法推荐的 平台sug词条,其中包含待评估的词条。
-
----
-
-# 工作流程
-
-## 第一步:理解综合意图
-
--   仔细阅读 <需求上下文> 与 <原始 query 问题>;
--   明确 <原始 query 问题> 的核心意图、主题焦点和任务目标;
--   提炼 <需求上下文> 所描述的核心任务、场景、概念和语义范围;
--   **将 <原始 query 问题> 和 <需求上下文> 融合成一个单一的、完整的“综合意图”**,作为后续评估的唯一基准。这个综合意图代表了用户在特定场景下最核心、最全面的需求。
-
-## 第二步:逐一评估 Sug 词条的综合相关度满足度
-
-对于 我给你的sug词条:
-
-1.  **评估其与“综合意图”的相关度满足度:**
-    -   将当前 sug 词条的语义与第一步中确定的“综合意图”(<原始 query 问题> 和 <需求上下文> 的结合体)进行比较。
-    -   全面评估该 sug 词条在主题、目标动作、语义范围、场景匹配、核心概念覆盖等各方面与“综合意图”的匹配和满足程度。
-    -   给出 0-1 之间的浮点数,代表其“相关度满足度”。数值比越高,表示该 sug 词条越能充分且精准地满足“综合意图”。
-
-**评估标准一致性要求:在评估 sug 词条时,请务必保持你对“相关度满足度”的判断标准和百分比给定的逻辑完全一致和稳定。**
-
-**相关性依据要求:** 简短叙述相关性依据,说明主要匹配点或差异点,以及该 sug 词条如何满足或未能完全满足“综合意图”的哪些方面。
-"""
-
-@dataclass
-class EvaluationFeedback:
-    reason: str=Field(..., description="简明扼要的理由")
-    score: float=Field(..., description="评估结果,1表示等价,0表示不等价,中间值表示部分等价")
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_insrtuctions,
-    output_type=EvaluationFeedback,
-)
-
-@function_tool
-async def get_query_suggestions(wrapper: RunContextWrapper[RunContext], query: str):
-    """Fetch search recommendations from Xiaohongshu."""
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-    query_suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-    print(query_suggestions)
-    async def evaluate_single_query(q_sug: str, q_with_context: str):
-        """Evaluate a single query suggestion."""
-        eval_input = f"""
-{q_with_context}
-<待评估的推荐query>
-{q_sug}
-</待评估的推荐query>
-        """
-        evaluator_result = await Runner.run(evaluator, eval_input)
-        result: EvaluationFeedback = evaluator_result.final_output
-        return {
-            "query": q_sug,
-            "score": result.score,
-            "reason": result.reason,
-        }
-
-    # 并发执行所有评估任务
-    q_with_context = wrapper.context.q_with_context
-    res = []
-    if query_suggestions:
-        res = await asyncio.gather(*[evaluate_single_query(q_sug, q_with_context) for q_sug in query_suggestions])
-    else:
-        res = '未返回任何推荐词'
-
-    # 记录到 RunContext
-    wrapper.context.operations_history.append({
-        "operation_type": "get_query_suggestions",
-        "timestamp": datetime.now().isoformat(),
-        "query": query,
-        "suggestions": query_suggestions,
-        "evaluations": res,
-    })
-
-    return res
-
-@function_tool
-def modify_query(wrapper: RunContextWrapper[RunContext], original_query: str, operation_type: str, new_query: str, reason: str):
-    """
-    Modify the search query with a specific operation.
-
-    Args:
-        original_query: The original query before modification
-        operation_type: Type of modification - must be one of: "简化", "扩展", "替换", "组合"
-        new_query: The modified query after applying the operation
-        reason: Detailed explanation of why this modification was made and what insight from previous suggestions led to this change
-
-    Returns:
-        A dict containing the modification record and the new query to use for next search
-    """
-    operation_types = ["简化", "扩展", "替换", "组合"]
-    if operation_type not in operation_types:
-        return {
-            "status": "error",
-            "message": f"Invalid operation_type. Must be one of: {', '.join(operation_types)}"
-        }
-
-    modification_record = {
-        "original_query": original_query,
-        "operation_type": operation_type,
-        "new_query": new_query,
-        "reason": reason,
-    }
-
-    # 记录到 RunContext
-    wrapper.context.operations_history.append({
-        "operation_type": "modify_query",
-        "timestamp": datetime.now().isoformat(),
-        "modification_type": operation_type,
-        "original_query": original_query,
-        "new_query": new_query,
-        "reason": reason,
-    })
-
-    return {
-        "status": "success",
-        "modification_record": modification_record,
-        "new_query": new_query,
-        "message": f"Query modified successfully. Use '{new_query}' for the next search."
-    }
-
-insrtuctions = """
-你是一个专业的搜索query优化专家,擅长通过动态探索找到最符合用户搜索习惯的query。
-
-## 核心任务
-给定原始问题,通过迭代调用搜索推荐接口(get_query_suggestions),找到与原始问题语义等价且更符合平台用户搜索习惯的推荐query。
-
-## 重要说明
-- **你不需要自己评估query的等价性**
-- get_query_suggestions 函数内部已集成评估子agent,会自动对每个推荐词进行评估
-- 返回结果包含:query(推荐词)、score(评分,1表示等价,0表示不等价)、reason(评估理由)
-- **你的职责是分析评估结果,做出决策和策略调整**
-
-## 防止幻觉 - 关键原则
-- **严禁编造数据**:只能基于 get_query_suggestions 实际返回的结果进行分析
-- **空结果处理**:如果返回的列表为空([]),必须明确说明"未返回任何推荐词"
-- **不要猜测**:在 modify_query 的 reason 中,不能引用不存在的推荐词或评分
-- **如实记录**:每次分析都要如实反映实际返回的数据
-
-## 工作流程
-
-### 1. 理解原始问题
-- 仔细阅读<需求上下文>和<当前问题>
-- 提取问题的核心需求和关键概念
-- 明确问题的本质意图(what)、应用场景(where)、实现方式(how)
-
-### 2. 动态探索策略
-
-**第一轮尝试:**
-- 使用原始问题直接调用 get_query_suggestions(query="原始问题")
-- **检查返回结果**:
-  - 如果返回空列表 []:说明"该query未返回任何推荐词",需要简化或替换query
-  - 如果有推荐词:查看每个推荐词的 score 和 reason
-- **做出判断**:是否有 score >= 0.8 的高分推荐词?
-
-**后续迭代:**
-如果没有高分推荐词(或返回空列表),必须先调用 modify_query 记录修改,然后再次搜索:
-
-**工具使用流程:**
-1. **分析评估反馈**(必须基于实际返回的数据):
-   - **情况A - 返回空列表**:
-     * 在 reason 中说明:"第X轮未返回任何推荐词,可能是query过于复杂或生僻"
-     * 不能编造任何推荐词或评分
-   - **情况B - 有推荐词但无高分**:
-     * 哪些推荐词得分较高?具体是多少分?评估理由是什么?
-     * 哪些推荐词偏离了原问题?如何偏离的?
-     * 推荐词整体趋势是什么?(过于泛化/具体化/领域偏移等)
-
-2. **决策修改策略**:基于实际评估反馈,调用 modify_query(original_query, operation_type, new_query, reason)
-   - reason 必须引用具体的数据,不能编造
-
-3. 使用返回的 new_query 调用 get_query_suggestions
-
-4. 分析新的评估结果,如果仍不满足,重复步骤1-3
-
-**四种操作类型(operation_type):**
-- **简化**:删除冗余词汇,提取核心关键词(当推荐词过于发散时)
-- **扩展**:添加限定词或场景描述(当推荐词过于泛化时)
-- **替换**:使用同义词、行业术语或口语化表达(当推荐词偏离核心时)
-- **组合**:调整关键词顺序或组合方式(当推荐词结构不合理时)
-
-**每次修改的reason必须包含:**
-- 上一轮评估结果的关键发现(引用具体的score和reason)
-- 基于评估反馈,为什么这样修改
-- 预期这次修改会带来什么改进
-
-### 3. 决策标准
-- **score >= 0.8**:认为该推荐词与原问题等价,可以作为最终结果
-- **0.5 <= score < 0.8**:部分等价,分析reason看是否可接受
-- **score < 0.5**:不等价,需要继续优化
-
-### 4. 迭代终止条件
-- **成功终止**:找到至少一个 score >= 0.8 的推荐query
-- **尝试上限**:最多迭代5轮,避免无限循环
-- **无推荐词**:推荐接口返回空列表或错误
-
-### 5. 输出要求
-
-**成功找到等价query时,输出格式:**
-```
-原始问题:[原问题]
-优化后的query:[最终找到的等价推荐query]
-评分:[score]
-```
-
-**未找到等价query时,输出格式:**
-```
-原始问题:[原问题]
-结果:未找到完全等价的推荐query
-建议:[简要建议,如:直接使用原问题搜索 或 使用最接近的推荐词]
-```
-
-## 注意事项
-- **第一轮必须使用原始问题**:直接调用 get_query_suggestions(query="原始问题")
-- **后续修改必须调用 modify_query**:不能直接用新query调用 get_query_suggestions
-- **重点关注评估结果**:每次都要仔细分析返回的 score 和 reason
-- **基于数据决策**:修改策略必须基于评估反馈,不能凭空猜测
-- **引用具体评分**:在分析和决策时,引用具体的score数值和reason内容
-- **优先选择高分推荐词**:score >= 0.8 即可认为等价
-- **严禁编造数据**:
-  * 如果返回空列表,必须在 reason 中明确说明"未返回任何推荐词"
-  * 不能引用不存在的推荐词、评分或评估理由
-  * 每次 modify_query 的 reason 必须基于上一轮实际返回的结果
-""".strip()
-
-
-
-
-async def main(input_dir: str):
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]  # 去掉 .py 后缀
-
-    # 日志保存到输入目录的 output/版本/时间戳 目录下
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-    agent = Agent[RunContext](
-        name="Query Optimization Agent",
-        instructions=insrtuctions,
-        tools=[get_query_suggestions, modify_query],
-       
-    )
-    result = await Runner.run(agent, input=q_with_context,  context = run_context,)
-    print(result.final_output)
-
-    # 保存最终输出到 RunContext
-    run_context.final_output = str(result.final_output)
-
-    # 保存 RunContext 到 log_dir
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    args = parser.parse_args()
-
-    asyncio.run(main(args.input_dir))

+ 0 - 650
sug_v2_with_eval_yx_v2.py

@@ -1,650 +0,0 @@
-import asyncio
-import json
-import os
-import argparse
-from datetime import datetime
-
-from agents import Agent, Runner, function_tool
-from lib.my_trace import set_trace
-from typing import Literal
-from pydantic import BaseModel, Field
-
-
-from lib.utils import read_file_as_string
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-from agents import Agent, RunContextWrapper, Runner, function_tool
-
-from pydantic import BaseModel, Field
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射,如 {'context_file': '...', 'q_file': '...'}")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-    # 中间数据记录 - 按时间顺序记录所有操作
-    operations_history: list[dict] = Field(default_factory=list, description="记录所有操作的历史,包括 get_query_suggestions 和 modify_query")
-    # 最终输出结果
-    final_output: str | None = Field(default=None, description="Agent的最终输出结果")
-
-
-eval_insrtuctions = """
-# 角色定义
-
-你是一个 **专业的语言专家和语义相关性评判专家**。
-
-你的任务是:判断我给你的平台sug词条,其与 <原始 query 问题> 相关度满足度百分比。
-
----
-
-# 输入信息
-
-你将接收到以下输入:
-1.  <需求上下文>:文档的解构结果或语义说明,用于理解 query 所针对的核心任务与场景。
-2.  <原始 query 问题>:用户的初始查询问题。
-3.  <平台sug词条>:模型或算法推荐的 平台sug词条,其中包含待评估的词条。
-
----
-
-# 判定流程
-
-## 第一层:判定是否在寻找知识(是或者否)
-
-### 什么是知识
-- 在社交媒体创作场景下,知识是指:
-- 知识 = 可应用的认知内容 + 实践指导 + 问题解决方案
-- 具体包含三个核心要素:
-  - 陈述性知识(Know-What): 是什么、有哪些、包括什么
-  - 程序性知识(Know-How): 如何做、怎么实现、步骤方法
-  - 策略性知识(Know-Why): 为什么、原理机制、优化策略
-
-### 判定方法:"是否在寻找知识"
-
-**三步判定法:**
-
-#### Step 1: 意图识别
-原始需求是想【知道/学会/获得】某样东西吗?
-- yes → 进入step2
-
-#### Step 2: 动词解析
-提取核心动词:
-- 认知类动词(包含但不限于:了解、学习、理解)
-- 操作类动词(包含但不限于:制作、拍摄、剪辑、运营)
-- 获取类动词(包含但不限于:找、下载、获取、收集)
-- 决策类动词(包含但不限于:选择、对比、评估)
-
-- 有明确动词 → 是知识需求
-- 无明确动词但有隐含目的 → 提取隐含动词
-- 完全无动作意图 → 非知识需求
-
-#### Step 3: 目标验证
-验证问题:
-1. 这个query解决后,用户会获得新的认知或能力吗? → YES则是知识
-2. 这个query的答案可以被学习和应用吗? → YES则是知识
-3. 这个query在寻求某个问题的解决方案吗? → YES则是知识
-
-**判定结果输出:**
-- ✅ **是知识需求** → 进入第二层
-- ❌ **非知识需求** → 停止判定,给出原因
-
----
-
-## 第二层:知识动机判定层(0-1分值)
-
-### 层级定义
-**本层目标:** 完全理解原始query的综合意图,识别主要需求和次要需求,并进行加权
-
-### 判定方法
-
-#### 维度1: 核心意图动词识别(权重50%)
-提取方法:
-1. 显性动词直接提取
-   - 例: "如何获取素材" → 核心动作="获取"
-
-2. 隐性动词语义推理
-   - 例: "川西秋天风光摄影" → 隐含动作="拍摄"或"欣赏"
-   → 需结合上下文判断
-
-3. 动作层级区分
-   - 主动作 vs 子动作
-   - 例: "如何用手机拍摄并剪辑vlog"
-     → 主动作="制作vlog"
-     → 子动作="拍摄"+"剪辑"
-
-评分规则:
-- 核心动作完全一致 → 1.0
-- 核心动作语义相近(近义词) → 0.8-0.9
-- 核心动作有包含关系(主次关系) → 0.5-0.7
-- 核心动作完全不同 → 0-0.4
-
-#### 维度2: 目标对象识别(权重30%)
-提取方法:
-1. 主要对象(What)
-   - 例: "获取川西秋季风光摄影素材"
-     → 主要对象="风光摄影素材"
-
-2. 对象的限定词(修饰语)
-   - 地域限定: "川西"
-   - 时间限定: "秋季"
-   - 质量限定: "高质量"
-
-评分规则:
-- 主要对象+核心限定词完全匹配 → 1.0
-- 主要对象匹配,限定词部分匹配 → 0.7-0.9
-- 主要对象匹配,限定词缺失/不符 → 0.4-0.6
-- 主要对象不匹配 → 0-0.3
-
-#### 维度3: 终极目的识别(权重20%)
-评分规则:
-- 目的完全一致 → 1.0
-- 目的相关但路径不同 → 0.6-0.7
-- 目的不相关 → 0-0.5
-
-### 综合意图加权计算
-
-**公式:**
-满足度匹配度 = 核心意图动词识别×0.5 + 目标对象识别×0.3 + 终极目的得分×0.2
-
-**阈值判定:**
-- **大于等于0.9** → 意图高度匹配,进入第三层
-- **小于0.9** → 意图不匹配,建议重新生成sug词,或者调整query词
-
-### 注意你的判定标准需要严格,你在遇到所有用例时你的评估标准是一致的
-
----
-
-## 第三层:品类相关性判定层(百分比制)
-
-### 层级定义
-**本层目标:** 基于第二层的综合意图,评估sug词条对原始query的满足程度
-
-### 评分标准体系
-
-#### 高度满足: 90%-100%
-**定义标准:**
-核心要素匹配度:
-✓ 核心动作: 完全一致或为标准近义词
-✓ 目标对象: 主体+关键限定词全部匹配
-✓ 使用场景: 完全相同或高度兼容
-✓ 终极目的: 完全一致
-
-**判定方法:**
-1. 逐一核对五维度得分,所有维度≥0.9
-2. 整体语义做"替换测试":把sug词替换原query,意思不变
-3. 用户视角验证:普通用户会认为"这就是我要的"
-
-#### 中高满足: 70%-89%
-**定义标准:**
-核心要素匹配度:
-✓ 核心动作: 一致或相近,但可能更泛化/具体化
-✓ 目标对象: 主体匹配,但1-2个限定词缺失/泛化
-✓ 使用场景: 基本兼容,可能略有扩展或收窄
-✓ 终极目的: 一致但实现路径略有差异
-
-**判定方法:**
-1. 检查是否有"泛化"或"具体化"现象
-   - 泛化: sug词范围更广,包含原query
-   - 具体化: sug词更聚焦,是原query的子集
-2. "有效信息保留率" ≥70%
-
-#### 中低满足: 40%-69%
-**定义标准:**
-核心要素匹配度:
-⚠ 核心动作: 存在明显差异,但主题相关
-⚠ 目标对象: 部分匹配,关键限定词缺失或错位
-⚠ 使用场景: 有关联但场景不同
-⚠ 终极目的: 相关但实现路径完全不同
-
-**判定方法:**
-1. 只有主题词重叠,核心动作不符
-2. 用户需要显著改变搜索策略
-3. 用户会觉得"有点相关但不是我要的"
-
-#### 低度/不满足: 0%-39%
-**定义标准:**
-核心要素匹配度:
-✗ 核心动作: 完全不同或对立
-✗ 目标对象: 主体不同或无关联
-✗ 使用场景: 场景冲突
-✗ 终极目的: 完全不相关
-
-**判定方法:**
-1. 除通用词外无有效重叠
-2. sug词满足了完全不同的需求
-3. 用户会直接忽略或感到困惑
-
-### 评分计算方法
-
-#### 维度计算得分
-相关性得分 = Σ(各维度得分 × 对应权重) × 100%
-
-其中权重分配:
-- 核心动作: 40%
-- 目标对象: 30% (主体+限定词)
-- 使用场景: 15%
-- 终极目的: 15%
-
-### 特殊情况处理规则
-
-#### 规则1: 泛化与具体化的区分
-泛化(sug词更广):
-- 如果原query的所有要素都在sug词覆盖范围内 → 中高满足档位
-- 泛化导致信息过载 → 适当降档
-
-具体化(sug词更窄):
-- 如果sug词是原query的典型子场景 → 中高满足档位
-- 具体化偏离主要需求 → 中低满足或更低
-
-#### 规则2: 同义转换的宽容度
-允许的同义转换:
-- 核心动作的近义词(如: 获取/下载/寻找/收集)
-- 目标对象的近义词(如: 技巧/方法/教程/攻略)
-- 行业内通用的同义表达
-
-不允许的转换(视为动作不符):
-- 动作意图改变(如: 获取 ≠ 制作)
-- 动作层级错位(如: 学习 ≠ 查看)
-- 目标对象改变(如: 工具推荐 ≠ 使用教程)
-
-#### 规则3: 多意图处理
-如果原query包含多个并列意图:
-1. 识别主次意图(通过语序、连接词判断)
-2. sug词至少满足主意图 → 基本可定义为中高满足
-3. sug词同时满足主次意图 → 可定义为高度满足
-4. sug词只满足次意图 → 降档处理
-
----
-
-# 输出要求
-
-输出结果必须为一个 JSON 格式,包含以下内容:
-
-```json
-{
-  "第一层_知识识别": {
-    "是否为知识需求": "是/否",
-    "判定依据": "简要说明判定理由,包括意图识别、动词提取、目标验证三步的关键发现"
-  },
-
-  "第二层_知识动机判定": {
-    "得分": "0-1之间的小数",
-    "判定依据": "简要说明各维度得分情况"
-  },
-
-  "第三层_相关性评分": {
-    "相关度满足度百分比": "0-100之间的整数",
-    "相关度满足度依据": "简要说明该sug词条如何与原始query和需求上下文共同形成的综合意图相匹配"
-  }
-}
-```
-
-**重要提示:请严格保持评估标准的一致性,在所有用例中使用相同的判定逻辑。**
-"""
-
-class KnowledgeRecognition(BaseModel):
-    """第一层:知识识别"""
-    是否为知识需求: str = Field(..., description="是/否")
-    判定依据: str = Field(..., description="简要说明判定理由")
-
-class MotivationJudgment(BaseModel):
-    """第二层:知识动机判定"""
-    得分: float = Field(..., description="0-1之间的小数", ge=0, le=1)
-    判定依据: str = Field(..., description="简要说明各维度得分情况")
-
-class RelevanceScore(BaseModel):
-    """第三层:相关性评分"""
-    相关度满足度百分比: int = Field(..., description="0-100之间的整数", ge=0, le=100)
-    相关度满足度依据: str = Field(..., description="简要说明匹配情况")
-
-class EvaluationFeedback(BaseModel):
-    """完整的三层评估反馈"""
-    第一层_知识识别: KnowledgeRecognition
-    第二层_知识动机判定: MotivationJudgment
-    第三层_相关性评分: RelevanceScore
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_insrtuctions,
-    output_type=EvaluationFeedback,
-)
-
-@function_tool
-async def get_query_suggestions(wrapper: RunContextWrapper[RunContext], query: str):
-    """Fetch search recommendations from Xiaohongshu."""
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-    query_suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-    print(query_suggestions)
-
-    async def evaluate_single_query(q_sug: str, q_with_context: str):
-        """Evaluate a single query suggestion."""
-        eval_input = f"""
-{q_with_context}
-<待评估的推荐query>
-{q_sug}
-</待评估的推荐query>
-        """
-        evaluator_result = await Runner.run(evaluator, eval_input)
-        result: EvaluationFeedback = evaluator_result.final_output
-
-        # 提取三层评估结果
-        layer1 = result.第一层_知识识别
-        layer2 = result.第二层_知识动机判定
-        layer3 = result.第三层_相关性评分
-
-        return {
-            "query": q_sug,
-            "第一层_知识识别": {
-                "是否为知识需求": layer1.是否为知识需求,
-                "判定依据": layer1.判定依据,
-            },
-            "第二层_知识动机判定": {
-                "得分": layer2.得分,
-                "判定依据": layer2.判定依据,
-            },
-            "第三层_相关性评分": {
-                "相关度满足度百分比": layer3.相关度满足度百分比,
-                "相关度满足度依据": layer3.相关度满足度依据,
-            },
-            # 为了向后兼容,保留旧的 score 字段(使用第三层百分比除以100)
-            "score": layer3.相关度满足度百分比 / 100.0,
-        }
-
-    # 并发执行所有评估任务
-    q_with_context = wrapper.context.q_with_context
-    res = []
-    if query_suggestions:
-        res = await asyncio.gather(*[evaluate_single_query(q_sug, q_with_context) for q_sug in query_suggestions])
-    else:
-        res = '未返回任何推荐词'
-
-    # 记录到 RunContext
-    wrapper.context.operations_history.append({
-        "operation_type": "get_query_suggestions",
-        "timestamp": datetime.now().isoformat(),
-        "query": query,
-        "suggestions": query_suggestions,
-        "evaluations": res,
-    })
-
-    return res
-
-@function_tool
-def modify_query(wrapper: RunContextWrapper[RunContext], original_query: str, operation_type: str, new_query: str, reason: str):
-    """
-    Modify the search query with a specific operation.
-
-    Args:
-        original_query: The original query before modification
-        operation_type: Type of modification - must be one of: "简化", "扩展", "替换", "组合"
-        new_query: The modified query after applying the operation
-        reason: Detailed explanation of why this modification was made and what insight from previous suggestions led to this change
-
-    Returns:
-        A dict containing the modification record and the new query to use for next search
-    """
-    operation_types = ["简化", "扩展", "替换", "组合"]
-    if operation_type not in operation_types:
-        return {
-            "status": "error",
-            "message": f"Invalid operation_type. Must be one of: {', '.join(operation_types)}"
-        }
-
-    modification_record = {
-        "original_query": original_query,
-        "operation_type": operation_type,
-        "new_query": new_query,
-        "reason": reason,
-    }
-
-    # 记录到 RunContext
-    wrapper.context.operations_history.append({
-        "operation_type": "modify_query",
-        "timestamp": datetime.now().isoformat(),
-        "modification_type": operation_type,
-        "original_query": original_query,
-        "new_query": new_query,
-        "reason": reason,
-    })
-
-    return {
-        "status": "success",
-        "modification_record": modification_record,
-        "new_query": new_query,
-        "message": f"Query modified successfully. Use '{new_query}' for the next search."
-    }
-
-insrtuctions = """
-你是一个专业的搜索query优化专家,擅长通过动态探索找到最符合用户搜索习惯的query。
-
-## 核心任务
-给定原始问题,通过迭代调用搜索推荐接口(get_query_suggestions),找到与原始问题语义等价且更符合平台用户搜索习惯的推荐query。
-
-## 重要说明
-- **你不需要自己评估query的等价性**
-- get_query_suggestions 函数内部已集成评估子agent,会自动对每个推荐词进行**三层评估**
-- 返回结果包含三层评估信息:
-  - **第一层_知识识别**:判断是否为知识需求(是/否)
-  - **第二层_知识动机判定**:综合意图匹配度得分(0-1分值)
-  - **第三层_相关性评分**:相关度满足度百分比(0-100%)
-  - **score**:为兼容旧逻辑保留的字段(第三层百分比/100)
-- **你的职责是分析三层评估结果,做出决策和策略调整**
-
-## 三层评估结果的解读
-
-### 第一层:知识识别
-- 如果"是否为知识需求"为"否",说明该推荐词不符合知识类需求,直接忽略
-
-### 第二层:知识动机判定(重要过滤层)
-- **高分(接近1.0)**:意图高度匹配,说明核心动词、目标对象、终极目的都基本吻合
-- **中等偏上(0.8-0.9)**:意图部分匹配,可能某些维度有偏差
-- **中等偏下或低分(< 0.8)**:意图明显偏离,核心要素不匹配,考虑调整query
-
-### 第三层:相关性评分
-根据评估标准分为四个档位:
-- **高度满足(90-100%)**:所有核心要素高度匹配,可作为最终结果
-- **中高满足(70-89%)**:核心要素基本匹配但有泛化/具体化,可作为备选
-- **中低满足(40-69%)**:部分相关但存在明显差异
-- **低度满足(0-39%)**:严重偏离,不建议使用
-
-## 防止幻觉 - 关键原则
-- **严禁编造数据**:只能基于 get_query_suggestions 实际返回的结果进行分析
-- **空结果处理**:如果返回的列表为空([]),必须明确说明"未返回任何推荐词"
-- **不要猜测**:在 modify_query 的 reason 中,不能引用不存在的推荐词或评分
-- **如实记录**:每次分析都要如实反映实际返回的数据
-
-## 工作流程
-
-### 1. 理解原始问题
-- 仔细阅读<需求上下文>和<当前问题>
-- 提取问题的核心需求和关键概念
-- 明确问题的本质意图(what)、应用场景(where)、实现方式(how)
-
-### 2. 动态探索策略
-
-**第一轮尝试:**
-- 使用原始问题直接调用 get_query_suggestions(query="原始问题")
-- **检查返回结果**:
-  - 如果返回空列表 []:说明"该query未返回任何推荐词",需要简化或替换query
-  - 如果有推荐词:查看每个推荐词的三层评估结果
-- **做出判断**:是否有符合条件的高质量推荐词?
-  - 第一层:是否为知识需求 = "是"
-  - 第二层:得分较高(体现意图匹配度)
-  - 第三层:相关度满足度百分比较高(在"高度满足"档位)
-
-**后续迭代:**
-如果没有符合条件的高分推荐词(或返回空列表),必须先调用 modify_query 记录修改,然后再次搜索:
-
-**工具使用流程:**
-1. **分析三层评估反馈**(必须基于实际返回的数据):
-   - **情况A - 返回空列表**:
-     * 在 reason 中说明:"第X轮未返回任何推荐词,可能是query过于复杂或生僻"
-     * 不能编造任何推荐词或评分
-
-   - **情况B - 有推荐词但不符合条件**:
-     * **第一层过滤**:哪些推荐词"是否为知识需求"为"否"?直接忽略
-     * **第二层分析**:查看"知识动机判定得分",分析判定依据,了解意图匹配情况
-       - 得分较高但仍不够:哪些维度拉低了得分?
-       - 得分偏低:核心意图偏离,需要调整query方向
-     * **第三层分析**:分析"相关度满足度百分比"和相关度依据:
-       - 高度满足档位:可作为最终结果
-       - 中高满足档位:评估是否可接受作为备选
-       - 中低及以下:分析偏离的具体原因(泛化/具体化/动作不符/对象错位等)
-     * 推荐词整体趋势是什么?(过于泛化/具体化/领域偏移等)
-
-2. **决策修改策略**:基于实际三层评估反馈,调用 modify_query(original_query, operation_type, new_query, reason)
-   - reason 必须引用具体的三层数据,不能编造
-   - 例如:"第1轮共返回5个推荐词,2个通过第一层知识识别,其第二层得分分别为X和Y,判定依据显示[具体原因],第三层相关度为Z%,依据为[具体内容]..."
-
-3. 使用返回的 new_query 调用 get_query_suggestions
-
-4. 分析新的三层评估结果,如果仍不满足,重复步骤1-3
-
-**四种操作类型(operation_type):**
-- **简化**:删除冗余词汇,提取核心关键词(当推荐词过于发散时)
-- **扩展**:添加限定词或场景描述(当推荐词过于泛化时)
-- **替换**:使用同义词、行业术语或口语化表达(当推荐词偏离核心时)
-- **组合**:调整关键词顺序或组合方式(当推荐词结构不合理时)
-
-**每次修改的reason必须包含:**
-- 上一轮评估结果的关键发现(引用具体的score和reason)
-- 基于评估反馈,为什么这样修改
-- 预期这次修改会带来什么改进
-
-### 3. 决策标准(基于三层评估)
-
-**优先级判断:**
-1. **首先检查第一层**:是否为知识需求 = "是"(否则直接忽略)
-2. **然后检查第二层**:知识动机判定得分,综合评估意图匹配度
-   - 得分高:核心动词、目标对象、终极目的基本吻合
-   - 得分中等:部分维度匹配,需分析判定依据
-   - 得分低:意图明显偏离,考虑调整query
-3. **最后检查第三层**:相关度满足度百分比,查看所属档位
-   - **高度满足档位**:核心要素高度匹配,可作为最终结果
-   - **中高满足档位**:基本匹配但有泛化/具体化,可作为备选
-   - **中低满足档位**:部分相关但存在明显差异,需要继续优化
-   - **低度满足档位**:严重偏离
-
-**综合判断:**
-- 理想结果:第一层通过 + 第二层高分 + 第三层高度满足档位
-- 可接受结果:第一层通过 + 第二层较好 + 第三层中高满足档位(作为备选)
-- 需要优化:任何一层表现不佳
-
-### 4. 迭代终止条件
-- **成功终止**:找到至少一个高质量的推荐query(三层评估均表现良好)
-- **尝试上限**:最多迭代5轮,避免无限循环
-- **无推荐词**:推荐接口返回空列表或错误
-
-### 5. 输出要求
-
-**成功找到高质量query时,输出格式:**
-```
-原始问题:[原问题]
-优化后的query:[最终找到的推荐query]
-三层评估结果:
-- 第一层(知识识别):[是/否],[判定依据]
-- 第二层(知识动机判定):得分 [X.X],[判定依据摘要]
-- 第三层(相关性评分):[X]%([满足档位]),[相关度依据摘要]
-```
-
-**未找到理想query时,输出格式:**
-```
-原始问题:[原问题]
-结果:未找到完全符合条件的推荐query
-最佳备选(如有):[最接近的推荐词]
-  - 三层评估:第一层[结果]、第二层[得分]、第三层[百分比%]
-建议:[基于实际情况的建议]
-```
-
-## 注意事项
-- **第一轮必须使用原始问题**:直接调用 get_query_suggestions(query="原始问题")
-- **后续修改必须调用 modify_query**:不能直接用新query调用 get_query_suggestions
-- **重点关注三层评估结果**:每次都要仔细分析三层评估数据
-- **基于数据决策**:修改策略必须基于三层评估反馈,不能凭空猜测
-- **引用具体评分**:在分析和决策时,引用具体的三层数据
-  * 第一层:是否为知识需求及判定依据
-  * 第二层:知识动机判定得分及判定依据
-  * 第三层:相关度满足度百分比及相关度依据
-- **分层过滤**:按照第一层→第二层→第三层的顺序逐层分析
-- **严禁编造数据**:
-  * 如果返回空列表,必须在 reason 中明确说明"未返回任何推荐词"
-  * 不能引用不存在的推荐词、评分或评估理由
-  * 每次 modify_query 的 reason 必须基于上一轮实际返回的三层评估结果
-  * 必须引用实际的数值和判定依据,不能编造
-""".strip()
-
-
-
-
-async def main(input_dir: str):
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]  # 去掉 .py 后缀
-
-    # 日志保存到输入目录的 output/版本/时间戳 目录下
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-    agent = Agent[RunContext](
-        name="Query Optimization Agent",
-        instructions=insrtuctions,
-        tools=[get_query_suggestions, modify_query],
-       
-    )
-    result = await Runner.run(agent, input=q_with_context,  context = run_context,)
-    print(result.final_output)
-
-    # 保存最终输出到 RunContext
-    run_context.final_output = str(result.final_output)
-
-    # 保存 RunContext 到 log_dir
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    args = parser.parse_args()
-
-    asyncio.run(main(args.input_dir))

+ 0 - 312
sug_v2_with_requery_yx.py

@@ -1,312 +0,0 @@
-import asyncio
-import json
-import os
-import argparse
-from datetime import datetime
-
-from agents import Agent, Runner, function_tool
-from lib.my_trace import set_trace
-from typing import Literal
-from dataclasses import dataclass
-from pydantic import BaseModel, Field
-
-
-from lib.utils import read_file_as_string
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-from agents import Agent, RunContextWrapper, Runner, function_tool
-
-from pydantic import BaseModel, Field
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射,如 {'context_file': '...', 'q_file': '...'}")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-    # 中间数据记录 - 按时间顺序记录所有操作
-    operations_history: list[dict] = Field(default_factory=list, description="记录所有操作的历史,包括 get_query_suggestions 和 modify_query")
-    # 最终输出结果
-    final_output: str | None = Field(default=None, description="Agent的最终输出结果")
-
-
-eval_insrtuctions = """
-你是一个专业的评估专家,负责评估给定的搜索query是否满足原始问题和需求,给出出评分和简明扼要的理由。
-"""
-
-@dataclass
-class EvaluationFeedback:
-    reason: str=Field(..., description="简明扼要的理由")
-    score: float=Field(..., description="评估结果,1表示等价,0表示不等价,中间值表示部分等价")
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_insrtuctions,
-    output_type=EvaluationFeedback,
-)
-
-@function_tool
-async def get_query_suggestions(wrapper: RunContextWrapper[RunContext], query: str):
-    """Fetch search recommendations from Xiaohongshu."""
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-    query_suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-    print(query_suggestions)
-    async def evaluate_single_query(q_sug: str, q_with_context: str):
-        """Evaluate a single query suggestion."""
-        eval_input = f"""
-{q_with_context}
-<待评估的推荐query>
-{q_sug}
-</待评估的推荐query>
-        """
-        evaluator_result = await Runner.run(evaluator, eval_input)
-        result: EvaluationFeedback = evaluator_result.final_output
-        return {
-            "query": q_sug,
-            "score": result.score,
-            "reason": result.reason,
-        }
-
-    # 并发执行所有评估任务
-    q_with_context = wrapper.context.q_with_context
-    res = []
-    if query_suggestions:
-        res = await asyncio.gather(*[evaluate_single_query(q_sug, q_with_context) for q_sug in query_suggestions])
-    else:
-        res = '未返回任何推荐词'
-
-    # 记录到 RunContext
-    wrapper.context.operations_history.append({
-        "operation_type": "get_query_suggestions",
-        "timestamp": datetime.now().isoformat(),
-        "query": query,
-        "suggestions": query_suggestions,
-        "evaluations": res,
-    })
-
-    return res
-
-@function_tool
-def modify_query(wrapper: RunContextWrapper[RunContext], original_query: str, operation_type: str, new_query: str, reason: str):
-    """
-    Modify the search query with a specific operation.
-
-    Args:
-        original_query: The original query before modification
-        operation_type: Type of modification - must be one of: "简化", "扩展", "替换", "组合"
-        new_query: The modified query after applying the operation
-        reason: Detailed explanation of why this modification was made and what insight from previous suggestions led to this change
-
-    Returns:
-        A dict containing the modification record and the new query to use for next search
-    """
-    operation_types = ["简化", "扩展", "替换", "组合"]
-    if operation_type not in operation_types:
-        return {
-            "status": "error",
-            "message": f"Invalid operation_type. Must be one of: {', '.join(operation_types)}"
-        }
-
-    modification_record = {
-        "original_query": original_query,
-        "operation_type": operation_type,
-        "new_query": new_query,
-        "reason": reason,
-    }
-
-    # 记录到 RunContext
-    wrapper.context.operations_history.append({
-        "operation_type": "modify_query",
-        "timestamp": datetime.now().isoformat(),
-        "modification_type": operation_type,
-        "original_query": original_query,
-        "new_query": new_query,
-        "reason": reason,
-    })
-
-    return {
-        "status": "success",
-        "modification_record": modification_record,
-        "new_query": new_query,
-        "message": f"Query modified successfully. Use '{new_query}' for the next search."
-    }
-
-insrtuctions = """
-你是一个专业的搜索query优化专家,擅长通过动态探索找到最符合用户搜索习惯的query。
-
-## 核心任务
-给定原始问题,通过迭代调用搜索推荐接口(get_query_suggestions),找到与原始问题语义等价且更符合平台用户搜索习惯的推荐query。
-
-## 重要说明
-- **你不需要自己评估query的等价性**
-- get_query_suggestions 函数内部已集成评估子agent,会自动对每个推荐词进行评估
-- 返回结果包含:query(推荐词)、score(评分,1表示等价,0表示不等价)、reason(评估理由)
-- **你的职责是分析评估结果,做出决策和策略调整**
-
-## 防止幻觉 - 关键原则
-- **严禁编造数据**:只能基于 get_query_suggestions 实际返回的结果进行分析
-- **空结果处理**:如果返回的列表为空([]),必须明确说明"未返回任何推荐词"
-- **不要猜测**:在 modify_query 的 reason 中,不能引用不存在的推荐词或评分
-- **如实记录**:每次分析都要如实反映实际返回的数据
-
-## 工作流程
-
-### 1. 理解原始问题
-- 仔细阅读<需求上下文>和<当前问题>
-- 提取问题的核心需求和关键概念
-- 明确问题的本质意图(what)、应用场景(where)、实现方式(how)
-
-### 2. 动态探索策略
-
-**第一轮尝试:**
-- 使用原始问题直接调用 get_query_suggestions(query="原始问题")
-- **检查返回结果**:
-  - 如果返回空列表 []:说明"该query未返回任何推荐词",需要简化或替换query
-  - 如果有推荐词:查看每个推荐词的 score 和 reason
-- **做出判断**:是否有 score >= 0.8 的高分推荐词?
-
-**后续迭代:**
-如果没有高分推荐词(或返回空列表),必须先调用 modify_query 记录修改,然后再次搜索:
-
-**工具使用流程:**
-1. **分析评估反馈**(必须基于实际返回的数据):
-   - **情况A - 返回空列表**:
-     * 在 reason 中说明:"第X轮未返回任何推荐词,可能是query过于复杂或生僻"
-     * 不能编造任何推荐词或评分
-   - **情况B - 有推荐词但无高分**:
-     * 哪些推荐词得分较高?具体是多少分?评估理由是什么?
-     * 哪些推荐词偏离了原问题?如何偏离的?
-     * 推荐词整体趋势是什么?(过于泛化/具体化/领域偏移等)
-
-2. **决策修改策略**:基于实际评估反馈,调用 modify_query(original_query, operation_type, new_query, reason)
-   - reason 必须引用具体的数据,不能编造
-
-3. 使用返回的 new_query 调用 get_query_suggestions
-
-4. 分析新的评估结果,如果仍不满足,重复步骤1-3
-
-**四种操作类型(operation_type):**
-- **简化**:删除冗余词汇,提取核心关键词(当推荐词过于发散时)
-- **扩展**:添加限定词或场景描述(当推荐词过于泛化时)
-- **替换**:使用同义词、行业术语或口语化表达(当推荐词偏离核心时)
-- **组合**:调整关键词顺序或组合方式(当推荐词结构不合理时)
-
-**每次修改的reason必须包含:**
-- 上一轮评估结果的关键发现(引用具体的score和reason)
-- 基于评估反馈,为什么这样修改
-- 预期这次修改会带来什么改进
-
-### 3. 决策标准
-- **score >= 0.8**:认为该推荐词与原问题等价,可以作为最终结果
-- **0.5 <= score < 0.8**:部分等价,分析reason看是否可接受
-- **score < 0.5**:不等价,需要继续优化
-
-### 4. 迭代终止条件
-- **成功终止**:找到至少一个 score >= 0.8 的推荐query
-- **尝试上限**:最多迭代5轮,避免无限循环
-- **无推荐词**:推荐接口返回空列表或错误
-
-### 5. 输出要求
-
-**成功找到等价query时,输出格式:**
-```
-原始问题:[原问题]
-优化后的query:[最终找到的等价推荐query]
-评分:[score]
-```
-
-**未找到等价query时,输出格式:**
-```
-原始问题:[原问题]
-结果:未找到完全等价的推荐query
-建议:[简要建议,如:直接使用原问题搜索 或 使用最接近的推荐词]
-```
-
-## 注意事项
-- **第一轮必须使用原始问题**:直接调用 get_query_suggestions(query="原始问题")
-- **后续修改必须调用 modify_query**:不能直接用新query调用 get_query_suggestions
-- **重点关注评估结果**:每次都要仔细分析返回的 score 和 reason
-- **基于数据决策**:修改策略必须基于评估反馈,不能凭空猜测
-- **引用具体评分**:在分析和决策时,引用具体的score数值和reason内容
-- **优先选择高分推荐词**:score >= 0.8 即可认为等价
-- **严禁编造数据**:
-  * 如果返回空列表,必须在 reason 中明确说明"未返回任何推荐词"
-  * 不能引用不存在的推荐词、评分或评估理由
-  * 每次 modify_query 的 reason 必须基于上一轮实际返回的结果
-""".strip()
-
-
-
-
-async def main(input_dir: str):
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]  # 去掉 .py 后缀
-
-    # 日志保存到输入目录的 output/版本/时间戳 目录下
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-    agent = Agent[RunContext](
-        name="Query Optimization Agent",
-        instructions=insrtuctions,
-        tools=[get_query_suggestions, modify_query],
-       
-    )
-    result = await Runner.run(agent, input=q_with_context,  context = run_context,)
-    print(result.final_output)
-
-    # 保存最终输出到 RunContext
-    run_context.final_output = str(result.final_output)
-
-    # 保存 RunContext 到 log_dir
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    args = parser.parse_args()
-
-    asyncio.run(main(args.input_dir))

+ 0 - 378
sug_v3.py

@@ -1,378 +0,0 @@
-import asyncio
-import json
-import os
-import argparse
-from datetime import datetime
-
-from agents import Agent, Runner, function_tool
-from lib.my_trace import set_trace
-from typing import Literal
-from dataclasses import dataclass
-from pydantic import BaseModel, Field
-
-
-from lib.utils import read_file_as_string
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-from agents import Agent, RunContextWrapper, Runner, function_tool
-
-from pydantic import BaseModel, Field
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射,如 {'context_file': '...', 'q_file': '...'}")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-    # 轮数计数器
-    current_round: int = Field(default=0, description="当前迭代轮数")
-    max_rounds: int = Field(default=100, description="最大迭代轮数")
-    # 中间数据记录 - 按时间顺序记录所有操作
-    operations_history: list[dict] = Field(default_factory=list, description="记录所有操作的历史,包括 get_query_suggestions 和 modify_query")
-    # 最终输出结果
-    final_output: str | None = Field(default=None, description="Agent的最终输出结果")
-
-
-eval_insrtuctions = """
-你是一个专业的评估专家,负责评估给定的搜索query是否满足原始问题和需求,给出出评分和简明扼要的理由。
-"""
-
-@dataclass
-class EvaluationFeedback:
-    reason: str=Field(..., description="简明扼要的理由")
-    score: float=Field(..., description="评估结果,1表示等价,0表示不等价,中间值表示部分等价")
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_insrtuctions,
-    output_type=EvaluationFeedback,
-)
-
-@function_tool
-async def get_query_suggestions(wrapper: RunContextWrapper[RunContext], query: str):
-    """Fetch search recommendations from Xiaohongshu."""
-    # 递增轮数
-    wrapper.context.current_round += 1
-    current_round = wrapper.context.current_round
-    max_rounds = wrapper.context.max_rounds
-
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-    query_suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-    print(query_suggestions)
-    async def evaluate_single_query(q_sug: str, q_with_context: str):
-        """Evaluate a single query suggestion."""
-        eval_input = f"""
-{q_with_context}
-<待评估的推荐query>
-{q_sug}
-</待评估的推荐query>
-        """
-        evaluator_result = await Runner.run(evaluator, eval_input)
-        result: EvaluationFeedback = evaluator_result.final_output
-        return {
-            "query": q_sug,
-            "score": result.score,
-            "reason": result.reason,
-        }
-
-    # 并发执行所有评估任务
-    q_with_context = wrapper.context.q_with_context
-    evaluations = []
-    if query_suggestions:
-        evaluations = await asyncio.gather(*[evaluate_single_query(q_sug, q_with_context) for q_sug in query_suggestions])
-    else:
-        evaluations = '未返回任何推荐词'
-
-    # 判断是否有满足要求的推荐词(score >= 0.8)
-    has_qualified = False
-    best_score = 0.0
-    best_query = None
-
-    if isinstance(evaluations, list) and len(evaluations) > 0:
-        for eval_item in evaluations:
-            if isinstance(eval_item, dict) and eval_item.get('score', 0) >= 0.8:
-                has_qualified = True
-                if eval_item.get('score', 0) > best_score:
-                    best_score = eval_item['score']
-                    best_query = eval_item['query']
-
-    # 决定是否需要继续优化
-    should_continue = not has_qualified and current_round < max_rounds
-
-    # 构建明确的指示信息
-    if has_qualified:
-        action = f"找到满足要求的推荐词!最佳推荐:'{best_query}'(评分:{best_score}),可以结束优化。"
-    elif current_round >= max_rounds:
-        action = "已达到最大轮数限制,必须停止优化。"
-    else:
-        action = "未找到满足要求的推荐词(score >= 0.8),请继续优化。"
-
-    # 记录到 RunContext
-    wrapper.context.operations_history.append({
-        "operation_type": "get_query_suggestions",
-        "timestamp": datetime.now().isoformat(),
-        "round": current_round,
-        "query": query,
-        "suggestions": query_suggestions,
-        "evaluations": evaluations,
-        "has_qualified": has_qualified,
-        "should_continue": should_continue,
-    })
-
-    # 返回结果,包含轮数信息和明确的行动指示
-    return {
-        "current_round": current_round,
-        "max_rounds": max_rounds,
-        "query": query,
-        "evaluations": evaluations,
-        "has_qualified": has_qualified,
-        "should_continue": should_continue,
-        "best_result": {"query": best_query, "score": best_score} if has_qualified else None,
-        "action": action,
-        "message": f"第 {current_round}/{max_rounds} 轮搜索完成。{action}"
-    }
-
-@function_tool
-def modify_query(wrapper: RunContextWrapper[RunContext], original_query: str, operation_type: str, new_query: str, reason: str):
-    """
-    Modify the search query with a specific operation.
-
-    Args:
-        original_query: The original query before modification
-        operation_type: Type of modification - must be one of: "简化", "扩展", "替换", "组合"
-        new_query: The modified query after applying the operation
-        reason: Detailed explanation of why this modification was made and what insight from previous suggestions led to this change
-
-    Returns:
-        A dict containing the modification record and the new query to use for next search
-    """
-    operation_types = ["简化", "扩展", "替换", "组合"]
-    if operation_type not in operation_types:
-        return {
-            "status": "error",
-            "message": f"Invalid operation_type. Must be one of: {', '.join(operation_types)}"
-        }
-
-    modification_record = {
-        "original_query": original_query,
-        "operation_type": operation_type,
-        "new_query": new_query,
-        "reason": reason,
-    }
-
-    # 记录到 RunContext
-    wrapper.context.operations_history.append({
-        "operation_type": "modify_query",
-        "timestamp": datetime.now().isoformat(),
-        "modification_type": operation_type,
-        "original_query": original_query,
-        "new_query": new_query,
-        "reason": reason,
-    })
-
-    return {
-        "status": "success",
-        "modification_record": modification_record,
-        "new_query": new_query,
-        "message": f"Query modified successfully. Use '{new_query}' for the next search."
-    }
-
-insrtuctions = """
-你是一个专业的搜索query优化专家,擅长通过动态探索找到最符合用户搜索习惯的query。
-
-## 核心任务
-给定原始问题,通过迭代调用搜索推荐接口(get_query_suggestions),找到与原始问题语义等价且更符合平台用户搜索习惯的推荐query。
-
-## 重要说明
-- **你不需要自己评估query的等价性,也不需要自己判断是否继续优化**
-- get_query_suggestions 函数内部已集成评估子agent,会自动完成评估和判断
-- **返回结果结构**:
-  - current_round: 当前迭代轮数
-  - max_rounds: 最大允许轮数(100轮)
-  - query: 本次搜索使用的query
-  - evaluations: 评估结果列表(仅供参考)
-  - **has_qualified**: 是否找到满足要求的推荐词(score >= 0.8)
-  - **should_continue**: 是否应该继续优化(true表示继续,false表示停止)
-  - **best_result**: 如果找到满足要求的推荐词,这里会显示最佳结果
-  - **action**: 明确的行动指示,告诉你接下来该做什么
-  - message: 完整的提示信息
-- **你的职责是遵循函数返回的指示**:
-  - 如果 should_continue = true,分析evaluations并调用 modify_query 继续优化
-  - 如果 should_continue = false 且 has_qualified = true,输出找到的最佳推荐词
-  - 如果 should_continue = false 且 has_qualified = false,说明已达到最大轮数但未找到满意结果
-
-## 防止幻觉 - 关键原则
-- **严禁编造数据**:只能基于 get_query_suggestions 实际返回的结果进行分析
-- **空结果处理**:如果返回的列表为空([]),必须明确说明"未返回任何推荐词"
-- **不要猜测**:在 modify_query 的 reason 中,不能引用不存在的推荐词或评分
-- **如实记录**:每次分析都要如实反映实际返回的数据
-
-## 工作流程
-
-### 1. 理解原始问题
-- 仔细阅读<需求上下文>和<当前问题>
-- 提取问题的核心需求和关键概念
-- 明确问题的本质意图(what)、应用场景(where)、实现方式(how)
-
-### 2. 动态探索策略
-
-**第一轮尝试:**
-- 使用原始问题直接调用 get_query_suggestions(query="原始问题")
-- **查看返回结果的关键字段**:
-  - **action**: 明确告诉你接下来该做什么
-  - **should_continue**: 是否需要继续优化
-  - **has_qualified**: 是否已找到满足要求的推荐词
-  - **best_result**: 如果找到了,这里会显示最佳推荐词和评分
-- **遵循指示行动**:直接按照 action 字段的指示执行
-
-**后续迭代:**
-如果 should_continue = true,必须先调用 modify_query 记录修改,然后再次调用 get_query_suggestions:
-
-**工具使用流程:**
-1. **分析评估反馈**(必须基于实际返回的数据):
-   - **情况A - 返回空列表**:
-     * 在 reason 中说明:"第X轮未返回任何推荐词,可能是query过于复杂或生僻"
-     * 不能编造任何推荐词或评分
-   - **情况B - 有推荐词但无高分**:
-     * 哪些推荐词得分较高?具体是多少分?评估理由是什么?
-     * 哪些推荐词偏离了原问题?如何偏离的?
-     * 推荐词整体趋势是什么?(过于泛化/具体化/领域偏移等)
-
-2. **决策修改策略**:基于实际评估反馈,调用 modify_query(original_query, operation_type, new_query, reason)
-   - reason 必须引用具体的数据,不能编造
-
-3. 使用返回的 new_query 调用 get_query_suggestions
-
-4. 分析新的评估结果,如果仍不满足,重复步骤1-3
-
-**四种操作类型(operation_type):**
-- **简化**:删除冗余词汇,提取核心关键词(当推荐词过于发散时)
-- **扩展**:添加限定词或场景描述(当推荐词过于泛化时)
-- **替换**:使用同义词、行业术语或口语化表达(当推荐词偏离核心时)
-- **组合**:调整关键词顺序或组合方式(当推荐词结构不合理时)
-
-**每次修改的reason必须包含:**
-- 上一轮评估结果的关键发现(引用具体的score和reason)
-- 基于评估反馈,为什么这样修改
-- 预期这次修改会带来什么改进
-
-### 3. 决策标准(由函数自动判断,你只需遵循)
-- 函数内部已自动按照 score >= 0.8 的标准判断是否找到满意的推荐词
-- 你只需要查看 has_qualified 和 should_continue 字段即可
-- 不需要自己遍历 evaluations 来判断分数
-
-### 4. 迭代终止条件(由函数自动判断)
-- 函数会自动判断何时应该停止或继续
-- **should_continue = false** 时必须停止:
-  - 情况1: has_qualified = true(找到了满意的推荐词)
-  - 情况2: current_round >= max_rounds(达到最大轮数)
-- **should_continue = true** 时应该继续优化
-
-### 5. 输出要求
-
-**成功找到等价query时,输出格式:**
-```
-原始问题:[原问题]
-优化后的query:[最终找到的等价推荐query]
-评分:[score]
-```
-
-**未找到等价query时,输出格式:**
-```
-原始问题:[原问题]
-结果:未找到完全等价的推荐query
-建议:[简要建议,如:直接使用原问题搜索 或 使用最接近的推荐词]
-```
-
-## 注意事项
-- **第一轮必须使用原始问题**:直接调用 get_query_suggestions(query="原始问题")
-- **后续修改必须调用 modify_query**:不能直接用新query调用 get_query_suggestions
-- **遵循函数返回的指示**:
-  - 重点关注 **should_continue** 和 **action** 字段
-  - 不需要自己判断是否继续优化,函数已经帮你判断好了
-  - 如果 should_continue = true,就继续优化;如果 false,就停止
-- **分析 evaluations 用于优化策略**:虽然不需要自己判断是否满足要求,但需要分析 evaluations 来决定如何修改query
-- **基于数据决策**:修改策略必须基于评估反馈,不能凭空猜测
-- **引用具体评分**:在 modify_query 的 reason 中,引用具体的score数值和reason内容
-- **坚持优化**:只要 should_continue = true,就应该继续尝试不同策略
-- **严禁编造数据**:
-  * 如果返回空列表,必须在 reason 中明确说明"未返回任何推荐词"
-  * 不能引用不存在的推荐词、评分或评估理由
-  * 每次 modify_query 的 reason 必须基于上一轮实际返回的结果
-""".strip()
-
-
-
-
-async def main(input_dir: str):
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]  # 去掉 .py 后缀
-
-    # 日志保存到输入目录的 output/版本/时间戳 目录下
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-    agent = Agent[RunContext](
-        name="Query Optimization Agent",
-        instructions=insrtuctions,
-        tools=[get_query_suggestions, modify_query],
-       
-    )
-    result = await Runner.run(agent, input=q_with_context,  context = run_context, max_turns=200)
-    print(result.final_output)
-
-    # 保存最终输出到 RunContext
-    run_context.final_output = str(result.final_output)
-
-    # 保存 RunContext 到 log_dir
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    args = parser.parse_args()
-
-    asyncio.run(main(args.input_dir))

+ 0 - 507
sug_v4.py

@@ -1,507 +0,0 @@
-import asyncio
-import json
-import os
-import argparse
-from datetime import datetime
-
-from agents import Agent, Runner, function_tool, AgentOutputSchema
-from lib.my_trace import set_trace
-from typing import Literal
-from pydantic import BaseModel, Field
-
-
-from lib.utils import read_file_as_string
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-from agents import Agent, RunContextWrapper, Runner, function_tool
-
-from pydantic import BaseModel, Field
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射,如 {'context_file': '...', 'q_file': '...'}")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-    # 问题标注结果 - 直接用字符串记录
-    question_annotation: str | None = Field(default=None, description="问题的标注结果,类似NER格式")
-    # 中间数据记录 - 按时间顺序记录所有操作
-    operations_history: list[dict] = Field(default_factory=list, description="记录所有操作的历史,包括 get_query_suggestions 和 modify_query")
-    # 最终输出结果
-    final_output: str | None = Field(default=None, description="Agent的最终输出结果")
-
-
-# 问题标注 Agent - 三层标注
-question_annotation_instructions = """
-你是搜索需求分析专家。给定问题(含需求背景),在原文上标注三层:本质、硬性、软性。
-
-## 三层结构
-
-**[本质]** - 问题的核心意图,改变后是完全不同的问题
-- 如何获取、教程、推荐、作品、测评等
-
-**[硬]** - 在本质意图下,必须满足的约束
-- 地域、时间、对象、质量要求等
-
-**[软]** - 可有可无的修饰
-- 能体现、特色、快速、简单等
-
-## 输出格式
-
-词语[本质-描述]、词语[硬-描述]、词语[软-描述]
-
-## 示例
-
-输入:如何获取能体现川西秋季特色的高质量风光摄影素材?
-输出:如何获取[本质-找方法] 川西[硬-地域] 秋季[硬-季节] 高质量[硬-质量] 风光摄影素材[硬-对象] 能体现[软-修饰] 特色[软-修饰]
-
-输入:PS抠图教程
-输出:PS[硬-工具] 抠图[硬-需求] 教程[本质-学习]
-
-输入:川西秋季风光摄影作品
-输出:川西[硬-地域] 秋季[硬-季节] 风光摄影[硬-对象] 作品[本质-欣赏]
-
-## 注意
-- 只输出标注后的字符串
-- 结合需求背景判断意图
-""".strip()
-
-question_annotator = Agent[None](
-    name="问题标注专家",
-    instructions=question_annotation_instructions,
-)
-
-eval_instructions = """
-你是搜索query评估专家。给定原始问题标注(三层)和推荐query,评估三个分数。
-
-## 评估目标
-
-用这个推荐query搜索,能否找到满足原始需求的内容?
-
-## 三层评分
-
-### 1. essence_score(本质/意图)= 0 或 1
-
-推荐query的本质/意图是否与原问题一致?
-
-**原问题标注中的[本质-XXX]:**
-- 找方法/如何获取 → 推荐词应该是方法/获取途径
-- 教程/学习 → 推荐词应该是教程/教学
-- 作品/欣赏 → 推荐词应该是作品展示
-- 工具/推荐 → 推荐词应该是工具推荐
-
-**评分:**
-- 1 = 本质一致
-- 0 = 本质改变(完全答非所问)
-
-**示例:**
-- 原问题:如何获取[本质-找方法]...素材
-- 推荐词:素材获取方法 → essence=1
-- 推荐词:素材推荐 → essence=1(都是获取途径)
-- 推荐词:素材作品 → essence=0(找方法≠看作品)
-
-### 2. hard_score(硬性约束)= 0 或 1
-
-在本质一致的前提下,是否满足所有硬性约束?
-
-**原问题标注中的[硬-XXX]:**地域、时间、对象、质量、工具(如果用户明确要求)等
-
-**评分:**
-- 1 = 所有硬性约束都满足
-- 0 = 任一硬性约束不满足
-
-**示例:**
-- 原问题:川西[硬-地域] 秋季[硬-季节] 高质量[硬-质量] 风光摄影[硬-对象]
-- 推荐词:川西秋季风光摄影 → hard=1
-- 推荐词:四川秋季风光摄影 → hard=0(地域泛化:川西→四川)
-- 推荐词:川西风光摄影 → hard=0(丢失季节)
-
-### 3. soft_score(软性修饰)= 0-1
-
-软性修饰词保留了多少?
-
-**原问题标注中的[软-XXX]:**修饰词、非关键限定等
-
-**评分参考:**
-- 1.0 = 完整保留
-- 0.7-0.9 = 保留核心
-- 0.4-0.6 = 部分丢失
-- 0-0.3 = 大量丢失
-
-## 示例
-
-**原问题标注:** 如何获取[本质-找方法] 川西[硬-地域] 秋季[硬-季节] 高质量[硬-质量] 风光摄影素材[硬-对象] 能体现[软-修饰] 特色[软-修饰]
-
-**推荐query1:** 川西秋季风光摄影素材视频
-- essence_score=0(找方法→找素材本身,本质变了)
-- hard_score=1(地域、季节、对象都符合)
-- soft_score=0.5(丢失"高质量")
-- reason: 本质改变,用户要找获取素材的方法,推荐词是素材内容本身
-
-**推荐query2:** 川西秋季风光摄影素材网站推荐
-- essence_score=1(找方法→推荐网站,本质一致)
-- hard_score=1(所有硬性约束满足)
-- soft_score=0.8(保留核心,"高质量"未明确但推荐通常筛选过)
-- reason: 本质一致,硬性约束满足,软性略有丢失但可接受
-
-## 注意
-
-- essence=0 直接拒绝,不管hard/soft多高
-- essence=1, hard=0 也要拒绝
-- essence=1, hard=1 才看soft_score
-""".strip()
-
-class EvaluationFeedback(BaseModel):
-    """评估反馈模型 - 三层评分"""
-    essence_score: Literal[0, 1] = Field(..., description="本质/意图匹配度,0或1。1=问题本质/意图一致,0=本质改变")
-    hard_score: Literal[0, 1] = Field(..., description="硬性约束匹配度,0或1。1=所有硬性约束都满足,0=任一硬性约束不满足")
-    soft_score: float = Field(..., description="软性修饰完整度,0-1的浮点数。1.0=完整保留,0.7-0.9=保留核心,0.4-0.6=泛化较大,0-0.3=大量丢失")
-    reason: str = Field(..., description="评估理由,包括:1)本质/意图是否一致 2)硬性约束是否满足 3)软性修饰保留情况 4)搜索预期")
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_instructions,
-    output_type=EvaluationFeedback,
-)
-
-@function_tool
-async def get_query_suggestions(wrapper: RunContextWrapper[RunContext], query: str):
-    """Fetch search recommendations from Xiaohongshu."""
-
-    # 1. 首次调用时,先标注问题(带需求背景)
-    if wrapper.context.question_annotation is None:
-        print("正在标注问题...")
-        annotation_result = await Runner.run(question_annotator, wrapper.context.q_with_context)
-        wrapper.context.question_annotation = str(annotation_result.final_output)
-        print(f"问题标注完成:{wrapper.context.question_annotation}")
-
-    # 2. 获取推荐词
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-    query_suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-    print(f"获取到 {len(query_suggestions) if query_suggestions else 0} 个推荐词:{query_suggestions}")
-
-    # 3. 评估推荐词(三层评分)
-    async def evaluate_single_query(q_sug: str):
-        """Evaluate a single query suggestion."""
-        eval_input = f"""
-<原始问题标注(三层)>
-{wrapper.context.question_annotation}
-</原始问题标注(三层)>
-
-<待评估的推荐query>
-{q_sug}
-</待评估的推荐query>
-
-请评估该推荐query的三个分数:
-1. essence_score: 本质/意图是否一致(0或1)
-2. hard_score: 硬性约束是否满足(0或1)
-3. soft_score: 软性修饰保留程度(0-1)
-4. reason: 详细的评估理由
-"""
-        evaluator_result = await Runner.run(evaluator, eval_input)
-        result: EvaluationFeedback = evaluator_result.final_output
-        return {
-            "query": q_sug,
-            "essence_score": result.essence_score,
-            "hard_score": result.hard_score,
-            "soft_score": result.soft_score,
-            "reason": result.reason,
-        }
-
-    # 并发执行所有评估任务
-    res = []
-    if query_suggestions:
-        res = await asyncio.gather(*[evaluate_single_query(q_sug) for q_sug in query_suggestions])
-    else:
-        res = '未返回任何推荐词'
-
-    # 记录到 RunContext
-    wrapper.context.operations_history.append({
-        "operation_type": "get_query_suggestions",
-        "timestamp": datetime.now().isoformat(),
-        "query": query,
-        "suggestions": query_suggestions,
-        "evaluations": res,
-    })
-
-    return res
-
-@function_tool
-def modify_query(wrapper: RunContextWrapper[RunContext], original_query: str, operation_type: str, new_query: str, reason: str):
-    """
-    Modify the search query with a specific operation.
-
-    Args:
-        original_query: The original query before modification
-        operation_type: Type of modification - must be one of: "简化", "扩展", "替换", "组合"
-        new_query: The modified query after applying the operation
-        reason: Detailed explanation of why this modification was made and what insight from previous suggestions led to this change
-
-    Returns:
-        A dict containing the modification record and the new query to use for next search
-    """
-    operation_types = ["简化", "扩展", "替换", "组合"]
-    if operation_type not in operation_types:
-        return {
-            "status": "error",
-            "message": f"Invalid operation_type. Must be one of: {', '.join(operation_types)}"
-        }
-
-    modification_record = {
-        "original_query": original_query,
-        "operation_type": operation_type,
-        "new_query": new_query,
-        "reason": reason,
-    }
-
-    # 记录到 RunContext
-    wrapper.context.operations_history.append({
-        "operation_type": "modify_query",
-        "timestamp": datetime.now().isoformat(),
-        "modification_type": operation_type,
-        "original_query": original_query,
-        "new_query": new_query,
-        "reason": reason,
-    })
-
-    return {
-        "status": "success",
-        "modification_record": modification_record,
-        "new_query": new_query,
-        "message": f"Query modified successfully. Use '{new_query}' for the next search."
-    }
-
-instructions = """
-你是一个专业的搜索query优化专家,擅长通过动态探索找到最符合用户搜索习惯的query。
-
-## 核心任务
-给定原始问题,通过迭代调用搜索推荐接口(get_query_suggestions),找到满足硬性要求且尽量保留软性信息的推荐query。
-
-## 重要说明
-- **你不需要自己评估query的适配性**
-- get_query_suggestions 函数会:
-  1. 首次调用时自动标注问题(三层:本质、硬性、软性)
-  2. 对每个推荐词进行三维度评估
-- 返回结果包含:
-  - **query**:推荐词
-  - **essence_score**:本质/意图匹配度(0或1),0=本质改变,1=本质一致
-  - **hard_score**:硬性约束匹配度(0或1),0=不满足约束,1=满足所有约束
-  - **soft_score**:软性修饰完整度(0-1),越高表示保留的信息越完整
-  - **reason**:详细的评估理由
-- **你的职责是分析评估结果,做出决策和策略调整**
-
-## 防止幻觉 - 关键原则
-- **严禁编造数据**:只能基于 get_query_suggestions 实际返回的结果进行分析
-- **空结果处理**:如果返回的列表为空([]),必须明确说明"未返回任何推荐词"
-- **不要猜测**:在 modify_query 的 reason 中,不能引用不存在的推荐词或评分
-- **如实记录**:每次分析都要如实反映实际返回的数据
-
-## 工作流程
-
-### 1. 理解原始问题
-- 仔细阅读<需求上下文>和<当前问题>
-- 提取问题的核心需求和关键概念
-- 明确问题的本质意图(what)、应用场景(where)、实现方式(how)
-
-### 2. 动态探索策略
-
-**第一轮尝试:**
-- 使用原始问题直接调用 get_query_suggestions(query="原始问题")
-- 第一次调用会自动标注问题(三层),后续调用会复用该标注
-- **检查返回结果**:
-  - 如果返回空列表 []:说明"该query未返回任何推荐词",需要简化或替换query
-  - 如果有推荐词:查看每个推荐词的 essence_score、hard_score、soft_score 和 reason
-- **做出判断**:是否有 essence_score=1 且 hard_score=1 且 soft_score >= 0.7 的推荐词?
-
-**后续迭代:**
-如果没有合格推荐词(或返回空列表),必须先调用 modify_query 记录修改,然后再次搜索:
-
-**工具使用流程:**
-1. **分析评估反馈**(必须基于实际返回的数据):
-   - **情况A - 返回空列表**:
-     * 在 reason 中说明:"第X轮未返回任何推荐词,可能是query过于复杂或生僻"
-     * 不能编造任何推荐词或评分
-
-   - **情况B - 有推荐词但无合格词**:
-     * **首先检查 essence_score**:
-       - 如果全是 essence_score=0:本质/意图完全不对,需要重新理解问题
-       - 如果有 essence_score=1:本质对了,继续分析
-
-     * **分析 essence_score=1 且 hard_score=1 的推荐词**:
-       - 有哪些?soft_score 是多少?
-       - 如果 soft_score 较低(<0.7),reason 中说明丢失了哪些信息?
-       - 能否通过修改query提高 soft_score?
-
-     * **如果 essence_score=1 但全是 hard_score=0**:
-       - reason 中说明了哪些硬性约束不满足?(地域、时间、对象、质量等)
-       - 需要如何调整query才能满足硬性约束?
-
-2. **决策修改策略**:基于实际评估反馈,调用 modify_query(original_query, operation_type, new_query, reason)
-   - reason 必须引用具体的 essence_score、hard_score、soft_score 和评估理由
-   - 不能编造任何数据
-
-3. 使用返回的 new_query 调用 get_query_suggestions
-
-4. 分析新的评估结果,如果仍不满足,重复步骤1-3
-
-**四种操作类型(operation_type):**
-- **简化**:删除冗余词汇,提取核心关键词(当推荐词过于发散时)
-- **扩展**:添加限定词或场景描述(当推荐词过于泛化时)
-- **替换**:使用同义词、行业术语或口语化表达(当推荐词偏离核心时)
-- **组合**:调整关键词顺序或组合方式(当推荐词结构不合理时)
-
-**每次修改的reason必须包含:**
-- 上一轮评估结果的关键发现(引用具体的 essence_score、hard_score、soft_score 和评估理由)
-- 基于评估反馈,为什么这样修改
-- 预期这次修改会带来什么改进
-
-### 3. 决策标准
-
-采用**三级评分标准**:
-
-**优先级1:本质/意图(最高优先级)**
-- **essence_score = 1**:本质一致,继续检查
-- **essence_score = 0**:本质改变,**直接放弃**
-
-**优先级2:硬性约束(必须满足)**
-- **hard_score = 1**:所有约束满足,继续检查
-- **hard_score = 0**:约束不满足,**直接放弃**
-
-**优先级3:软性修饰(越高越好)**
-- **soft_score >= 0.7**:信息保留较完整,**理想结果**
-- **0.5 <= soft_score < 0.7**:有所丢失但可接受,**备选结果**
-- **soft_score < 0.5**:丢失过多,继续优化
-
-**采纳标准:**
-- **最优**:essence=1 且 hard=1 且 soft >= 0.7
-- **可接受**:essence=1 且 hard=1 且 soft >= 0.5(多次尝试后)
-- **不可接受**:essence=0 或 hard=0(无论soft多高)
-
-### 4. 迭代终止条件
-- **成功终止**:找到 essence=1 且 hard=1 且 soft >= 0.7 的推荐query
-- **可接受终止**:5轮后找到 essence=1 且 hard=1 且 soft >= 0.5 的推荐query
-- **失败终止**:最多5轮
-- **无推荐词**:返回空列表或错误
-
-### 5. 输出要求
-
-**成功找到合格query时:**
-```
-原始问题:[原问题]
-优化后的query:[最终推荐query]
-本质匹配度:[essence_score] (1=本质一致)
-硬性约束匹配度:[hard_score] (1=所有约束满足)
-软性修饰完整度:[soft_score] (0-1)
-评估理由:[简要说明]
-```
-
-**未找到合格query时:**
-```
-原始问题:[原问题]
-结果:未找到合格推荐query
-原因:[本质不符 / 硬性约束不满足 / 软性信息丢失过多]
-最接近的推荐词:[如果有essence=1且hard=1的词,列出soft最高的]
-建议:[简要建议]
-```
-
-## 注意事项
-- **第一轮必须使用原始问题**:直接调用 get_query_suggestions(query="原始问题")
-  - 第一次调用会自动标注问题(三层),打印出标注结果
-  - 后续调用会复用该标注进行评估
-
-- **后续修改必须调用 modify_query**:不能直接用新query调用 get_query_suggestions
-
-- **重点关注评估结果**:每次都要仔细分析返回的三个分数
-  - **essence_score=0 直接放弃**,本质不对
-  - **hard_score=0 也直接放弃**,约束不满足
-  - **优先关注 essence=1 且 hard=1 的推荐词**,分析如何提升 soft_score
-
-- **基于数据决策**:修改策略必须基于评估反馈
-  - 引用具体的 essence_score、hard_score、soft_score
-  - 引用 reason 中的关键发现
-
-- **采纳标准明确**:
-  - **最优**:essence=1 且 hard=1 且 soft >= 0.7,立即采纳
-  - **可接受**:essence=1 且 hard=1 且 soft >= 0.5,多次尝试后可采纳
-  - **不可接受**:essence=0 或 hard=0,无论soft多高都不能用
-
-- **严禁编造数据**:
-  * 如果返回空列表,必须明确说明"未返回任何推荐词"
-  * 不能引用不存在的推荐词、分数或评估理由
-  * 每次 modify_query 的 reason 必须基于上一轮实际返回的结果
-""".strip()
-
-
-
-
-async def main(input_dir: str):
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]  # 去掉 .py 后缀
-
-    # 日志保存到输入目录的 output/版本/时间戳 目录下
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-    agent = Agent[RunContext](
-        name="Query Optimization Agent",
-        instructions=instructions,
-        tools=[get_query_suggestions, modify_query],
-    )
-    result = await Runner.run(agent, input=q_with_context,  context = run_context,)
-    print(result.final_output)
-
-    # 保存最终输出到 RunContext
-    run_context.final_output = str(result.final_output)
-
-    # 保存 RunContext 到 log_dir
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    args = parser.parse_args()
-
-    asyncio.run(main(args.input_dir))

+ 0 - 507
sug_v4_1.py

@@ -1,507 +0,0 @@
-import asyncio
-import json
-import os
-import argparse
-from datetime import datetime
-
-from agents import Agent, Runner, function_tool, AgentOutputSchema
-from lib.my_trace import set_trace
-from typing import Literal
-from pydantic import BaseModel, Field
-
-
-from lib.utils import read_file_as_string
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-from agents import Agent, RunContextWrapper, Runner, function_tool
-
-from pydantic import BaseModel, Field
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射,如 {'context_file': '...', 'q_file': '...'}")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-    # 问题标注结果 - 直接用字符串记录
-    question_annotation: str | None = Field(default=None, description="问题的标注结果,类似NER格式")
-    # 中间数据记录 - 按时间顺序记录所有操作
-    operations_history: list[dict] = Field(default_factory=list, description="记录所有操作的历史,包括 get_query_suggestions 和 modify_query")
-    # 最终输出结果
-    final_output: str | None = Field(default=None, description="Agent的最终输出结果")
-
-
-# 问题标注 Agent - 三层标注
-question_annotation_instructions = """
-你是搜索需求分析专家。给定问题(含需求背景),在原文上标注三层:本质、硬性、软性。
-
-## 三层结构
-
-**[本质]** - 问题的核心意图,改变后是完全不同的问题
-- 如何获取、教程、推荐、作品、测评等
-
-**[硬]** - 在本质意图下,必须满足的约束
-- 地域、时间、对象、质量要求等
-
-**[软]** - 可有可无的修饰
-- 能体现、特色、快速、简单等
-
-## 输出格式
-
-词语[本质-描述]、词语[硬-描述]、词语[软-描述]
-
-## 示例
-
-输入:如何获取能体现川西秋季特色的高质量风光摄影素材?
-输出:如何获取[本质-找方法] 川西[硬-地域] 秋季[硬-季节] 高质量[硬-质量] 风光摄影素材[硬-对象] 能体现[软-修饰] 特色[软-修饰]
-
-输入:PS抠图教程
-输出:PS[硬-工具] 抠图[硬-需求] 教程[本质-学习]
-
-输入:川西秋季风光摄影作品
-输出:川西[硬-地域] 秋季[硬-季节] 风光摄影[硬-对象] 作品[本质-欣赏]
-
-## 注意
-- 只输出标注后的字符串
-- 结合需求背景判断意图
-""".strip()
-
-question_annotator = Agent[None](
-    name="问题标注专家",
-    instructions=question_annotation_instructions,
-)
-
-eval_instructions = """
-你是搜索query评估专家。给定原始问题标注(三层)和推荐query,评估三个分数。
-
-## 评估目标
-
-用这个推荐query搜索,能否找到满足原始需求的内容?
-
-## 三层评分
-
-### 1. essence_score(本质/意图)= 0 或 1
-
-推荐query的本质/意图是否与原问题一致?
-
-**原问题标注中的[本质-XXX]:**
-- 找方法/如何获取 → 推荐词应该是方法/获取途径
-- 教程/学习 → 推荐词应该是教程/教学
-- 作品/欣赏 → 推荐词应该是作品展示
-- 工具/推荐 → 推荐词应该是工具推荐
-
-**评分:**
-- 1 = 本质一致
-- 0 = 本质改变(完全答非所问)
-
-**示例:**
-- 原问题:如何获取[本质-找方法]...素材
-- 推荐词:素材获取方法 → essence=1
-- 推荐词:素材推荐 → essence=1(都是获取途径)
-- 推荐词:素材作品 → essence=0(找方法≠看作品)
-
-### 2. hard_score(硬性约束)= 0 或 1
-
-在本质一致的前提下,是否满足所有硬性约束?
-
-**原问题标注中的[硬-XXX]:**地域、时间、对象、质量、工具(如果用户明确要求)等
-
-**评分:**
-- 1 = 所有硬性约束都满足
-- 0 = 任一硬性约束不满足
-
-**示例:**
-- 原问题:川西[硬-地域] 秋季[硬-季节] 高质量[硬-质量] 风光摄影[硬-对象]
-- 推荐词:川西秋季风光摄影 → hard=1
-- 推荐词:四川秋季风光摄影 → hard=0(地域泛化:川西→四川)
-- 推荐词:川西风光摄影 → hard=0(丢失季节)
-
-### 3. soft_score(软性修饰)= 0-1
-
-软性修饰词保留了多少?
-
-**原问题标注中的[软-XXX]:**修饰词、非关键限定等
-
-**评分参考:**
-- 1.0 = 完整保留
-- 0.7-0.9 = 保留核心
-- 0.4-0.6 = 部分丢失
-- 0-0.3 = 大量丢失
-
-## 示例
-
-**原问题标注:** 如何获取[本质-找方法] 川西[硬-地域] 秋季[硬-季节] 高质量[硬-质量] 风光摄影素材[硬-对象] 能体现[软-修饰] 特色[软-修饰]
-
-**推荐query1:** 川西秋季风光摄影素材视频
-- essence_score=0(找方法→找素材本身,本质变了)
-- hard_score=1(地域、季节、对象都符合)
-- soft_score=0.5(丢失"高质量")
-- reason: 本质改变,用户要找获取素材的方法,推荐词是素材内容本身
-
-**推荐query2:** 川西秋季风光摄影素材网站推荐
-- essence_score=1(找方法→推荐网站,本质一致)
-- hard_score=1(所有硬性约束满足)
-- soft_score=0.8(保留核心,"高质量"未明确但推荐通常筛选过)
-- reason: 本质一致,硬性约束满足,软性略有丢失但可接受
-
-## 注意
-
-- essence=0 直接拒绝,不管hard/soft多高
-- essence=1, hard=0 也要拒绝
-- essence=1, hard=1 才看soft_score
-""".strip()
-
-class EvaluationFeedback(BaseModel):
-    """评估反馈模型 - 三层评分"""
-    essence_score: Literal[0, 1] = Field(..., description="本质/意图匹配度,0或1。1=问题本质/意图一致,0=本质改变")
-    hard_score: Literal[0, 1] = Field(..., description="硬性约束匹配度,0或1。1=所有硬性约束都满足,0=任一硬性约束不满足")
-    soft_score: float = Field(..., description="软性修饰完整度,0-1的浮点数。1.0=完整保留,0.7-0.9=保留核心,0.4-0.6=泛化较大,0-0.3=大量丢失")
-    reason: str = Field(..., description="评估理由,包括:1)本质/意图是否一致 2)硬性约束是否满足 3)软性修饰保留情况 4)搜索预期")
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_instructions,
-    output_type=EvaluationFeedback,
-)
-
-@function_tool
-async def get_query_suggestions(wrapper: RunContextWrapper[RunContext], query: str):
-    """Fetch search recommendations from Xiaohongshu."""
-
-    # 1. 首次调用时,先标注问题(带需求背景)
-    if wrapper.context.question_annotation is None:
-        print("正在标注问题...")
-        annotation_result = await Runner.run(question_annotator, wrapper.context.q_with_context)
-        wrapper.context.question_annotation = str(annotation_result.final_output)
-        print(f"问题标注完成:{wrapper.context.question_annotation}")
-
-    # 2. 获取推荐词
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-    query_suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-    print(f"获取到 {len(query_suggestions) if query_suggestions else 0} 个推荐词:{query_suggestions}")
-
-    # 3. 评估推荐词(三层评分)
-    async def evaluate_single_query(q_sug: str):
-        """Evaluate a single query suggestion."""
-        eval_input = f"""
-<原始问题标注(三层)>
-{wrapper.context.question_annotation}
-</原始问题标注(三层)>
-
-<待评估的推荐query>
-{q_sug}
-</待评估的推荐query>
-
-请评估该推荐query的三个分数:
-1. essence_score: 本质/意图是否一致(0或1)
-2. hard_score: 硬性约束是否满足(0或1)
-3. soft_score: 软性修饰保留程度(0-1)
-4. reason: 详细的评估理由
-"""
-        evaluator_result = await Runner.run(evaluator, eval_input)
-        result: EvaluationFeedback = evaluator_result.final_output
-        return {
-            "query": q_sug,
-            "essence_score": result.essence_score,
-            "hard_score": result.hard_score,
-            "soft_score": result.soft_score,
-            "reason": result.reason,
-        }
-
-    # 并发执行所有评估任务
-    res = []
-    if query_suggestions:
-        res = await asyncio.gather(*[evaluate_single_query(q_sug) for q_sug in query_suggestions])
-    else:
-        res = '未返回任何推荐词'
-
-    # 记录到 RunContext
-    wrapper.context.operations_history.append({
-        "operation_type": "get_query_suggestions",
-        "timestamp": datetime.now().isoformat(),
-        "query": query,
-        "suggestions": query_suggestions,
-        "evaluations": res,
-    })
-
-    return res
-
-@function_tool
-def modify_query(wrapper: RunContextWrapper[RunContext], original_query: str, operation_type: str, new_query: str, reason: str):
-    """
-    Modify the search query with a specific operation.
-
-    Args:
-        original_query: The original query before modification
-        operation_type: Type of modification - must be one of: "简化", "扩展", "替换", "组合"
-        new_query: The modified query after applying the operation
-        reason: Detailed explanation of why this modification was made and what insight from previous suggestions led to this change
-
-    Returns:
-        A dict containing the modification record and the new query to use for next search
-    """
-    operation_types = ["简化", "扩展", "替换", "组合"]
-    if operation_type not in operation_types:
-        return {
-            "status": "error",
-            "message": f"Invalid operation_type. Must be one of: {', '.join(operation_types)}"
-        }
-
-    modification_record = {
-        "original_query": original_query,
-        "operation_type": operation_type,
-        "new_query": new_query,
-        "reason": reason,
-    }
-
-    # 记录到 RunContext
-    wrapper.context.operations_history.append({
-        "operation_type": "modify_query",
-        "timestamp": datetime.now().isoformat(),
-        "modification_type": operation_type,
-        "original_query": original_query,
-        "new_query": new_query,
-        "reason": reason,
-    })
-
-    return {
-        "status": "success",
-        "modification_record": modification_record,
-        "new_query": new_query,
-        "message": f"Query modified successfully. Use '{new_query}' for the next search."
-    }
-
-instructions = """
-你是一个专业的搜索query优化专家,擅长通过动态探索找到最符合用户搜索习惯的query。
-
-## 核心任务
-给定原始问题,通过迭代调用搜索推荐接口(get_query_suggestions),找到满足硬性要求且尽量保留软性信息的推荐query。
-
-## 重要说明
-- **你不需要自己评估query的适配性**
-- get_query_suggestions 函数会:
-  1. 首次调用时自动标注问题(三层:本质、硬性、软性)
-  2. 对每个推荐词进行三维度评估
-- 返回结果包含:
-  - **query**:推荐词
-  - **essence_score**:本质/意图匹配度(0或1),0=本质改变,1=本质一致
-  - **hard_score**:硬性约束匹配度(0或1),0=不满足约束,1=满足所有约束
-  - **soft_score**:软性修饰完整度(0-1),越高表示保留的信息越完整
-  - **reason**:详细的评估理由
-- **你的职责是分析评估结果,做出决策和策略调整**
-
-## 防止幻觉 - 关键原则
-- **严禁编造数据**:只能基于 get_query_suggestions 实际返回的结果进行分析
-- **空结果处理**:如果返回的列表为空([]),必须明确说明"未返回任何推荐词"
-- **不要猜测**:在 modify_query 的 reason 中,不能引用不存在的推荐词或评分
-- **如实记录**:每次分析都要如实反映实际返回的数据
-
-## 工作流程
-
-### 1. 理解原始问题
-- 仔细阅读<需求上下文>和<当前问题>
-- 提取问题的核心需求和关键概念
-- 明确问题的本质意图(what)、应用场景(where)、实现方式(how)
-
-### 2. 动态探索策略
-
-**第一轮尝试:**
-- 使用原始问题直接调用 get_query_suggestions(query="原始问题")
-- 第一次调用会自动标注问题(三层),后续调用会复用该标注
-- **检查返回结果**:
-  - 如果返回空列表 []:说明"该query未返回任何推荐词",需要简化或替换query
-  - 如果有推荐词:查看每个推荐词的 essence_score、hard_score、soft_score 和 reason
-- **做出判断**:是否有 essence_score=1 且 hard_score=1 且 soft_score >= 0.7 的推荐词?
-
-**后续迭代:**
-如果没有合格推荐词(或返回空列表),必须先调用 modify_query 记录修改,然后再次搜索:
-
-**工具使用流程:**
-1. **分析评估反馈**(必须基于实际返回的数据):
-   - **情况A - 返回空列表**:
-     * 在 reason 中说明:"第X轮未返回任何推荐词,可能是query过于复杂或生僻"
-     * 不能编造任何推荐词或评分
-
-   - **情况B - 有推荐词但无合格词**:
-     * **首先检查 essence_score**:
-       - 如果全是 essence_score=0:本质/意图完全不对,需要重新理解问题
-       - 如果有 essence_score=1:本质对了,继续分析
-
-     * **分析 essence_score=1 且 hard_score=1 的推荐词**:
-       - 有哪些?soft_score 是多少?
-       - 如果 soft_score 较低(<0.7),reason 中说明丢失了哪些信息?
-       - 能否通过修改query提高 soft_score?
-
-     * **如果 essence_score=1 但全是 hard_score=0**:
-       - reason 中说明了哪些硬性约束不满足?(地域、时间、对象、质量等)
-       - 需要如何调整query才能满足硬性约束?
-
-2. **决策修改策略**:基于实际评估反馈,调用 modify_query(original_query, operation_type, new_query, reason)
-   - reason 必须引用具体的 essence_score、hard_score、soft_score 和评估理由
-   - 不能编造任何数据
-
-3. 使用返回的 new_query 调用 get_query_suggestions
-
-4. 分析新的评估结果,如果仍不满足,重复步骤1-3
-
-**四种操作类型(operation_type):**
-- **简化**:删除冗余词汇,提取核心关键词(当推荐词过于发散时)
-- **扩展**:添加限定词或场景描述(当推荐词过于泛化时)
-- **替换**:使用同义词、行业术语或口语化表达(当推荐词偏离核心时)
-- **组合**:调整关键词顺序或组合方式(当推荐词结构不合理时)
-
-**每次修改的reason必须包含:**
-- 上一轮评估结果的关键发现(引用具体的 essence_score、hard_score、soft_score 和评估理由)
-- 基于评估反馈,为什么这样修改
-- 预期这次修改会带来什么改进
-
-### 3. 决策标准
-
-采用**三级评分标准**:
-
-**优先级1:本质/意图(最高优先级)**
-- **essence_score = 1**:本质一致,继续检查
-- **essence_score = 0**:本质改变,**直接放弃**
-
-**优先级2:硬性约束(必须满足)**
-- **hard_score = 1**:所有约束满足,继续检查
-- **hard_score = 0**:约束不满足,**直接放弃**
-
-**优先级3:软性修饰(越高越好)**
-- **soft_score >= 0.7**:信息保留较完整,**理想结果**
-- **0.5 <= soft_score < 0.7**:有所丢失但可接受,**备选结果**
-- **soft_score < 0.5**:丢失过多,继续优化
-
-**采纳标准:**
-- **最优**:essence=1 且 hard=1 且 soft >= 0.7
-- **可接受**:essence=1 且 hard=1 且 soft >= 0.5(多次尝试后)
-- **不可接受**:essence=0 或 hard=0(无论soft多高)
-
-### 4. 迭代终止条件
-- **成功终止**:找到 essence=1 且 hard=1 且 soft >= 0.7 的推荐query
-- **可接受终止**:5轮后找到 essence=1 且 hard=1 且 soft >= 0.5 的推荐query
-- **失败终止**:最多5轮
-- **无推荐词**:返回空列表或错误
-
-### 5. 输出要求
-
-**成功找到合格query时:**
-```
-原始问题:[原问题]
-优化后的query:[最终推荐query]
-本质匹配度:[essence_score] (1=本质一致)
-硬性约束匹配度:[hard_score] (1=所有约束满足)
-软性修饰完整度:[soft_score] (0-1)
-评估理由:[简要说明]
-```
-
-**未找到合格query时:**
-```
-原始问题:[原问题]
-结果:未找到合格推荐query
-原因:[本质不符 / 硬性约束不满足 / 软性信息丢失过多]
-最接近的推荐词:[如果有essence=1且hard=1的词,列出soft最高的]
-建议:[简要建议]
-```
-
-## 注意事项
-- **第一轮必须使用原始问题**:直接调用 get_query_suggestions(query="原始问题")
-  - 第一次调用会自动标注问题(三层),打印出标注结果
-  - 后续调用会复用该标注进行评估
-
-- **后续修改必须调用 modify_query**:不能直接用新query调用 get_query_suggestions
-
-- **重点关注评估结果**:每次都要仔细分析返回的三个分数
-  - **essence_score=0 直接放弃**,本质不对
-  - **hard_score=0 也直接放弃**,约束不满足
-  - **优先关注 essence=1 且 hard=1 的推荐词**,分析如何提升 soft_score
-
-- **基于数据决策**:修改策略必须基于评估反馈
-  - 引用具体的 essence_score、hard_score、soft_score
-  - 引用 reason 中的关键发现
-
-- **采纳标准明确**:
-  - **最优**:essence=1 且 hard=1 且 soft >= 0.7,立即采纳
-  - **可接受**:essence=1 且 hard=1 且 soft >= 0.5,多次尝试后可采纳
-  - **不可接受**:essence=0 或 hard=0,无论soft多高都不能用
-
-- **严禁编造数据**:
-  * 如果返回空列表,必须明确说明"未返回任何推荐词"
-  * 不能引用不存在的推荐词、分数或评估理由
-  * 每次 modify_query 的 reason 必须基于上一轮实际返回的结果
-""".strip()
-
-
-
-
-async def main(input_dir: str):
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]  # 去掉 .py 后缀
-
-    # 日志保存到输入目录的 output/版本/时间戳 目录下
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-    agent = Agent[RunContext](
-        name="Query Optimization Agent",
-        instructions=instructions,
-        tools=[get_query_suggestions, modify_query],
-    )
-    result = await Runner.run(agent, input=q_with_context,  context = run_context,)
-    print(result.final_output)
-
-    # 保存最终输出到 RunContext
-    run_context.final_output = str(result.final_output)
-
-    # 保存 RunContext 到 log_dir
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    args = parser.parse_args()
-
-    asyncio.run(main(args.input_dir))

+ 0 - 468
sug_v4_2.py

@@ -1,468 +0,0 @@
-import asyncio
-import json
-import os
-import argparse
-from datetime import datetime
-
-from agents import Agent, Runner, function_tool, AgentOutputSchema
-from lib.my_trace import set_trace
-from typing import Literal
-from pydantic import BaseModel, Field
-
-
-from lib.utils import read_file_as_string
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-from agents import Agent, RunContextWrapper, Runner, function_tool
-
-from pydantic import BaseModel, Field
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射,如 {'context_file': '...', 'q_file': '...'}")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-    # 问题标注结果 - 直接用字符串记录
-    question_annotation: str | None = Field(default=None, description="问题的标注结果,类似NER格式")
-    # 中间数据记录 - 按时间顺序记录所有操作
-    operations_history: list[dict] = Field(default_factory=list, description="记录所有操作的历史,包括 get_query_suggestions 和 modify_query")
-    # 最终输出结果
-    final_output: str | None = Field(default=None, description="Agent的最终输出结果")
-
-
-# 问题标注 Agent - 三层标注
-question_annotation_instructions = """
-你是搜索需求分析专家。给定问题(含需求背景),在原文上标注三层:本质、硬性、软性。
-
-## 三层结构
-
-**[本质]** - 问题的核心意图,改变后是完全不同的问题
-- 如何获取、教程、推荐、作品、测评等
-
-**[硬]** - 在本质意图下,必须满足的约束
-- 地域、时间、对象、质量要求等
-
-**[软]** - 可有可无的修饰
-- 能体现、特色、快速、简单等
-
-## 输出格式
-
-词语[本质-描述]、词语[硬-描述]、词语[软-描述]
-
-## 注意
-- 只输出标注后的字符串
-- 结合需求背景判断意图
-""".strip()
-
-question_annotator = Agent[None](
-    name="问题标注专家",
-    instructions=question_annotation_instructions,
-)
-
-eval_instructions = """
-你是搜索query评估专家。给定原始问题标注(三层)和推荐query,评估三个分数。
-
-## 评估目标
-
-用这个推荐query搜索,能否找到满足原始需求的内容?
-
-## 三层评分
-
-### 1. essence_score(本质/意图)= 0 或 1
-
-推荐query的本质/意图是否与原问题一致?
-
-**原问题标注中的[本质-XXX]:**
-- 找方法/如何获取 → 推荐词应该是方法/获取途径
-- 教程/学习 → 推荐词应该是教程/教学
-- 作品/欣赏 → 推荐词应该是作品展示
-- 工具/推荐 → 推荐词应该是工具推荐
-
-**评分:**
-- 1 = 本质一致
-- 0 = 本质改变(完全答非所问)
-
-### 2. hard_score(硬性约束)= 0 或 1
-
-在本质一致的前提下,是否满足所有硬性约束?
-
-**原问题标注中的[硬-XXX]:**地域、时间、对象、质量、工具(如果用户明确要求)等
-
-**评分:**
-- 1 = 所有硬性约束都满足
-- 0 = 任一硬性约束不满足
-
-### 3. soft_score(软性修饰)= 0-1
-
-软性修饰词保留了多少?
-
-**原问题标注中的[软-XXX]:**修饰词、非关键限定等
-
-**评分参考:**
-- 1.0 = 完整保留
-- 0.7-0.9 = 保留核心
-- 0.4-0.6 = 部分丢失
-- 0-0.3 = 大量丢失
-
-## 注意
-
-- essence=0 直接拒绝,不管hard/soft多高
-- essence=1, hard=0 也要拒绝
-- essence=1, hard=1 才看soft_score
-""".strip()
-
-class EvaluationFeedback(BaseModel):
-    """评估反馈模型 - 三层评分"""
-    essence_score: Literal[0, 1] = Field(..., description="本质/意图匹配度,0或1。1=问题本质/意图一致,0=本质改变")
-    hard_score: Literal[0, 1] = Field(..., description="硬性约束匹配度,0或1。1=所有硬性约束都满足,0=任一硬性约束不满足")
-    soft_score: float = Field(..., description="软性修饰完整度,0-1的浮点数。1.0=完整保留,0.7-0.9=保留核心,0.4-0.6=泛化较大,0-0.3=大量丢失")
-    reason: str = Field(..., description="评估理由,包括:1)本质/意图是否一致 2)硬性约束是否满足 3)软性修饰保留情况 4)搜索预期")
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_instructions,
-    output_type=EvaluationFeedback,
-)
-
-@function_tool
-async def get_query_suggestions(wrapper: RunContextWrapper[RunContext], query: str):
-    """Fetch search recommendations from Xiaohongshu."""
-
-    # 1. 首次调用时,先标注问题(带需求背景)
-    if wrapper.context.question_annotation is None:
-        print("正在标注问题...")
-        annotation_result = await Runner.run(question_annotator, wrapper.context.q_with_context)
-        wrapper.context.question_annotation = str(annotation_result.final_output)
-        print(f"问题标注完成:{wrapper.context.question_annotation}")
-
-    # 2. 获取推荐词
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-    query_suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-    print(f"获取到 {len(query_suggestions) if query_suggestions else 0} 个推荐词:{query_suggestions}")
-
-    # 3. 评估推荐词(三层评分)
-    async def evaluate_single_query(q_sug: str):
-        """Evaluate a single query suggestion."""
-        eval_input = f"""
-<原始问题标注(三层)>
-{wrapper.context.question_annotation}
-</原始问题标注(三层)>
-
-<待评估的推荐query>
-{q_sug}
-</待评估的推荐query>
-
-请评估该推荐query的三个分数:
-1. essence_score: 本质/意图是否一致(0或1)
-2. hard_score: 硬性约束是否满足(0或1)
-3. soft_score: 软性修饰保留程度(0-1)
-4. reason: 详细的评估理由
-"""
-        evaluator_result = await Runner.run(evaluator, eval_input)
-        result: EvaluationFeedback = evaluator_result.final_output
-        return {
-            "query": q_sug,
-            "essence_score": result.essence_score,
-            "hard_score": result.hard_score,
-            "soft_score": result.soft_score,
-            "reason": result.reason,
-        }
-
-    # 并发执行所有评估任务
-    res = []
-    if query_suggestions:
-        res = await asyncio.gather(*[evaluate_single_query(q_sug) for q_sug in query_suggestions])
-    else:
-        res = '未返回任何推荐词'
-
-    # 记录到 RunContext
-    wrapper.context.operations_history.append({
-        "operation_type": "get_query_suggestions",
-        "timestamp": datetime.now().isoformat(),
-        "query": query,
-        "suggestions": query_suggestions,
-        "evaluations": res,
-    })
-
-    return res
-
-@function_tool
-def modify_query(wrapper: RunContextWrapper[RunContext], original_query: str, operation_type: str, new_query: str, reason: str):
-    """
-    Modify the search query with a specific operation.
-
-    Args:
-        original_query: The original query before modification
-        operation_type: Type of modification - must be one of: "简化", "扩展", "替换", "组合"
-        new_query: The modified query after applying the operation
-        reason: Detailed explanation of why this modification was made and what insight from previous suggestions led to this change
-
-    Returns:
-        A dict containing the modification record and the new query to use for next search
-    """
-    operation_types = ["简化", "扩展", "替换", "组合"]
-    if operation_type not in operation_types:
-        return {
-            "status": "error",
-            "message": f"Invalid operation_type. Must be one of: {', '.join(operation_types)}"
-        }
-
-    modification_record = {
-        "original_query": original_query,
-        "operation_type": operation_type,
-        "new_query": new_query,
-        "reason": reason,
-    }
-
-    # 记录到 RunContext
-    wrapper.context.operations_history.append({
-        "operation_type": "modify_query",
-        "timestamp": datetime.now().isoformat(),
-        "modification_type": operation_type,
-        "original_query": original_query,
-        "new_query": new_query,
-        "reason": reason,
-    })
-
-    return {
-        "status": "success",
-        "modification_record": modification_record,
-        "new_query": new_query,
-        "message": f"Query modified successfully. Use '{new_query}' for the next search."
-    }
-
-instructions = """
-你是一个专业的搜索query优化专家,擅长通过动态探索找到最符合用户搜索习惯的query。
-
-## 核心任务
-给定原始问题,通过迭代调用搜索推荐接口(get_query_suggestions),找到满足硬性要求且尽量保留软性信息的推荐query。
-
-## 重要说明
-- **你不需要自己评估query的适配性**
-- get_query_suggestions 函数会:
-  1. 首次调用时自动标注问题(三层:本质、硬性、软性)
-  2. 对每个推荐词进行三维度评估
-- 返回结果包含:
-  - **query**:推荐词
-  - **essence_score**:本质/意图匹配度(0或1),0=本质改变,1=本质一致
-  - **hard_score**:硬性约束匹配度(0或1),0=不满足约束,1=满足所有约束
-  - **soft_score**:软性修饰完整度(0-1),越高表示保留的信息越完整
-  - **reason**:详细的评估理由
-- **你的职责是分析评估结果,做出决策和策略调整**
-
-## 防止幻觉 - 关键原则
-- **严禁编造数据**:只能基于 get_query_suggestions 实际返回的结果进行分析
-- **空结果处理**:如果返回的列表为空([]),必须明确说明"未返回任何推荐词"
-- **不要猜测**:在 modify_query 的 reason 中,不能引用不存在的推荐词或评分
-- **如实记录**:每次分析都要如实反映实际返回的数据
-
-## 工作流程
-
-### 1. 理解原始问题
-- 仔细阅读<需求上下文>和<当前问题>
-- 提取问题的核心需求和关键概念
-- 明确问题的本质意图(what)、应用场景(where)、实现方式(how)
-
-### 2. 动态探索策略
-
-**第一轮尝试:**
-- 使用原始问题直接调用 get_query_suggestions(query="原始问题")
-- 第一次调用会自动标注问题(三层),后续调用会复用该标注
-- **检查返回结果**:
-  - 如果返回空列表 []:说明"该query未返回任何推荐词",需要简化或替换query
-  - 如果有推荐词:查看每个推荐词的 essence_score、hard_score、soft_score 和 reason
-- **做出判断**:是否有 essence_score=1 且 hard_score=1 且 soft_score >= 0.7 的推荐词?
-
-**后续迭代:**
-如果没有合格推荐词(或返回空列表),必须先调用 modify_query 记录修改,然后再次搜索:
-
-**工具使用流程:**
-1. **分析评估反馈**(必须基于实际返回的数据):
-   - **情况A - 返回空列表**:
-     * 在 reason 中说明:"第X轮未返回任何推荐词,可能是query过于复杂或生僻"
-     * 不能编造任何推荐词或评分
-
-   - **情况B - 有推荐词但无合格词**:
-     * **首先检查 essence_score**:
-       - 如果全是 essence_score=0:本质/意图完全不对,需要重新理解问题
-       - 如果有 essence_score=1:本质对了,继续分析
-
-     * **分析 essence_score=1 且 hard_score=1 的推荐词**:
-       - 有哪些?soft_score 是多少?
-       - 如果 soft_score 较低(<0.7),reason 中说明丢失了哪些信息?
-       - 能否通过修改query提高 soft_score?
-
-     * **如果 essence_score=1 但全是 hard_score=0**:
-       - reason 中说明了哪些硬性约束不满足?(地域、时间、对象、质量等)
-       - 需要如何调整query才能满足硬性约束?
-
-2. **决策修改策略**:基于实际评估反馈,调用 modify_query(original_query, operation_type, new_query, reason)
-   - reason 必须引用具体的 essence_score、hard_score、soft_score 和评估理由
-   - 不能编造任何数据
-
-3. 使用返回的 new_query 调用 get_query_suggestions
-
-4. 分析新的评估结果,如果仍不满足,重复步骤1-3
-
-**四种操作类型(operation_type):**
-- **简化**:删除冗余词汇,提取核心关键词(当推荐词过于发散时)
-- **扩展**:添加限定词或场景描述(当推荐词过于泛化时)
-- **替换**:使用同义词、行业术语或口语化表达(当推荐词偏离核心时)
-- **组合**:调整关键词顺序或组合方式(当推荐词结构不合理时)
-
-**每次修改的reason必须包含:**
-- 上一轮评估结果的关键发现(引用具体的 essence_score、hard_score、soft_score 和评估理由)
-- 基于评估反馈,为什么这样修改
-- 预期这次修改会带来什么改进
-
-### 3. 决策标准
-
-采用**三级评分标准**:
-
-**优先级1:本质/意图(最高优先级)**
-- **essence_score = 1**:本质一致,继续检查
-- **essence_score = 0**:本质改变,**直接放弃**
-
-**优先级2:硬性约束(必须满足)**
-- **hard_score = 1**:所有约束满足,继续检查
-- **hard_score = 0**:约束不满足,**直接放弃**
-
-**优先级3:软性修饰(越高越好)**
-- **soft_score >= 0.7**:信息保留较完整,**理想结果**
-- **0.5 <= soft_score < 0.7**:有所丢失但可接受,**备选结果**
-- **soft_score < 0.5**:丢失过多,继续优化
-
-**采纳标准:**
-- **最优**:essence=1 且 hard=1 且 soft >= 0.7
-- **可接受**:essence=1 且 hard=1 且 soft >= 0.5(多次尝试后)
-- **不可接受**:essence=0 或 hard=0(无论soft多高)
-
-### 4. 迭代终止条件
-- **成功终止**:找到 essence=1 且 hard=1 且 soft >= 0.7 的推荐query
-- **可接受终止**:5轮后找到 essence=1 且 hard=1 且 soft >= 0.5 的推荐query
-- **失败终止**:最多5轮
-- **无推荐词**:返回空列表或错误
-
-### 5. 输出要求
-
-**成功找到合格query时:**
-```
-原始问题:[原问题]
-优化后的query:[最终推荐query]
-本质匹配度:[essence_score] (1=本质一致)
-硬性约束匹配度:[hard_score] (1=所有约束满足)
-软性修饰完整度:[soft_score] (0-1)
-评估理由:[简要说明]
-```
-
-**未找到合格query时:**
-```
-原始问题:[原问题]
-结果:未找到合格推荐query
-原因:[本质不符 / 硬性约束不满足 / 软性信息丢失过多]
-最接近的推荐词:[如果有essence=1且hard=1的词,列出soft最高的]
-建议:[简要建议]
-```
-
-## 注意事项
-- **第一轮必须使用原始问题**:直接调用 get_query_suggestions(query="原始问题")
-  - 第一次调用会自动标注问题(三层),打印出标注结果
-  - 后续调用会复用该标注进行评估
-
-- **后续修改必须调用 modify_query**:不能直接用新query调用 get_query_suggestions
-
-- **重点关注评估结果**:每次都要仔细分析返回的三个分数
-  - **essence_score=0 直接放弃**,本质不对
-  - **hard_score=0 也直接放弃**,约束不满足
-  - **优先关注 essence=1 且 hard=1 的推荐词**,分析如何提升 soft_score
-
-- **基于数据决策**:修改策略必须基于评估反馈
-  - 引用具体的 essence_score、hard_score、soft_score
-  - 引用 reason 中的关键发现
-
-- **采纳标准明确**:
-  - **最优**:essence=1 且 hard=1 且 soft >= 0.7,立即采纳
-  - **可接受**:essence=1 且 hard=1 且 soft >= 0.5,多次尝试后可采纳
-  - **不可接受**:essence=0 或 hard=0,无论soft多高都不能用
-
-- **严禁编造数据**:
-  * 如果返回空列表,必须明确说明"未返回任何推荐词"
-  * 不能引用不存在的推荐词、分数或评估理由
-  * 每次 modify_query 的 reason 必须基于上一轮实际返回的结果
-""".strip()
-
-
-
-
-async def main(input_dir: str):
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]  # 去掉 .py 后缀
-
-    # 日志保存到输入目录的 output/版本/时间戳 目录下
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-    agent = Agent[RunContext](
-        name="Query Optimization Agent",
-        instructions=instructions,
-        tools=[get_query_suggestions, modify_query],
-    )
-    result = await Runner.run(agent, input=q_with_context,  context = run_context,)
-    print(result.final_output)
-
-    # 保存最终输出到 RunContext
-    run_context.final_output = str(result.final_output)
-
-    # 保存 RunContext 到 log_dir
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    args = parser.parse_args()
-
-    asyncio.run(main(args.input_dir))

+ 0 - 507
sug_v4_with_yx.py

@@ -1,507 +0,0 @@
-import asyncio
-import json
-import os
-import argparse
-from datetime import datetime
-
-from agents import Agent, Runner, function_tool, AgentOutputSchema
-from lib.my_trace import set_trace
-from typing import Literal
-from pydantic import BaseModel, Field
-
-
-from lib.utils import read_file_as_string
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-from agents import Agent, RunContextWrapper, Runner, function_tool
-
-from pydantic import BaseModel, Field
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射,如 {'context_file': '...', 'q_file': '...'}")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-    # 问题标注结果 - 直接用字符串记录
-    question_annotation: str | None = Field(default=None, description="问题的标注结果,类似NER格式")
-    # 中间数据记录 - 按时间顺序记录所有操作
-    operations_history: list[dict] = Field(default_factory=list, description="记录所有操作的历史,包括 get_query_suggestions 和 modify_query")
-    # 最终输出结果
-    final_output: str | None = Field(default=None, description="Agent的最终输出结果")
-
-
-# 问题标注 Agent - 三层标注
-question_annotation_instructions = """
-你是搜索需求分析专家。给定问题(含需求背景),在原文上标注三层:本质、硬性、软性。
-
-## 三层结构
-
-**[本质]** - 问题的核心意图,改变后是完全不同的问题
-- 如何获取、教程、推荐、作品、测评等
-
-**[硬]** - 在本质意图下,必须满足的约束
-- 地域、时间、对象、质量要求等
-
-**[软]** - 可有可无的修饰
-- 能体现、特色、快速、简单等
-
-## 输出格式
-
-词语[本质-描述]、词语[硬-描述]、词语[软-描述]
-
-## 示例
-
-输入:如何获取能体现川西秋季特色的高质量风光摄影素材?
-输出:如何获取[本质-找方法] 川西[硬-地域] 秋季[硬-季节] 高质量[硬-质量] 风光摄影素材[硬-对象] 能体现[软-修饰] 特色[软-修饰]
-
-输入:PS抠图教程
-输出:PS[硬-工具] 抠图[硬-需求] 教程[本质-学习]
-
-输入:川西秋季风光摄影作品
-输出:川西[硬-地域] 秋季[硬-季节] 风光摄影[硬-对象] 作品[本质-欣赏]
-
-## 注意
-- 只输出标注后的字符串
-- 结合需求背景判断意图
-""".strip()
-
-question_annotator = Agent[None](
-    name="问题标注专家",
-    instructions=question_annotation_instructions,
-)
-
-eval_instructions = """
-你是搜索query评估专家。给定原始问题标注(三层)和推荐query,评估三个分数。
-
-## 评估目标
-
-用这个推荐query搜索,能否找到满足原始需求的内容?
-
-## 三层评分
-
-### 1. essence_score(本质/意图)= 0 或 1
-
-推荐query的本质/意图是否与原问题一致?
-
-**原问题标注中的[本质-XXX]:**
-- 找方法/如何获取 → 推荐词应该是方法/获取途径
-- 教程/学习 → 推荐词应该是教程/教学
-- 作品/欣赏 → 推荐词应该是作品展示
-- 工具/推荐 → 推荐词应该是工具推荐
-
-**评分:**
-- 1 = 本质一致
-- 0 = 本质改变(完全答非所问)
-
-**示例:**
-- 原问题:如何获取[本质-找方法]...素材
-- 推荐词:素材获取方法 → essence=1
-- 推荐词:素材推荐 → essence=1(都是获取途径)
-- 推荐词:素材作品 → essence=0(找方法≠看作品)
-
-### 2. hard_score(硬性约束)= 0 或 1
-
-在本质一致的前提下,是否满足所有硬性约束?
-
-**原问题标注中的[硬-XXX]:**地域、时间、对象、质量、工具(如果用户明确要求)等
-
-**评分:**
-- 1 = 所有硬性约束都满足
-- 0 = 任一硬性约束不满足
-
-**示例:**
-- 原问题:川西[硬-地域] 秋季[硬-季节] 高质量[硬-质量] 风光摄影[硬-对象]
-- 推荐词:川西秋季风光摄影 → hard=1
-- 推荐词:四川秋季风光摄影 → hard=0(地域泛化:川西→四川)
-- 推荐词:川西风光摄影 → hard=0(丢失季节)
-
-### 3. soft_score(软性修饰)= 0-1
-
-软性修饰词保留了多少?
-
-**原问题标注中的[软-XXX]:**修饰词、非关键限定等
-
-**评分参考:**
-- 1.0 = 完整保留
-- 0.7-0.9 = 保留核心
-- 0.4-0.6 = 部分丢失
-- 0-0.3 = 大量丢失
-
-## 示例
-
-**原问题标注:** 如何获取[本质-找方法] 川西[硬-地域] 秋季[硬-季节] 高质量[硬-质量] 风光摄影素材[硬-对象] 能体现[软-修饰] 特色[软-修饰]
-
-**推荐query1:** 川西秋季风光摄影素材视频
-- essence_score=0(找方法→找素材本身,本质变了)
-- hard_score=1(地域、季节、对象都符合)
-- soft_score=0.5(丢失"高质量")
-- reason: 本质改变,用户要找获取素材的方法,推荐词是素材内容本身
-
-**推荐query2:** 川西秋季风光摄影素材网站推荐
-- essence_score=1(找方法→推荐网站,本质一致)
-- hard_score=1(所有硬性约束满足)
-- soft_score=0.8(保留核心,"高质量"未明确但推荐通常筛选过)
-- reason: 本质一致,硬性约束满足,软性略有丢失但可接受
-
-## 注意
-
-- essence=0 直接拒绝,不管hard/soft多高
-- essence=1, hard=0 也要拒绝
-- essence=1, hard=1 才看soft_score
-""".strip()
-
-class EvaluationFeedback(BaseModel):
-    """评估反馈模型 - 三层评分"""
-    essence_score: Literal[0, 1] = Field(..., description="本质/意图匹配度,0或1。1=问题本质/意图一致,0=本质改变")
-    hard_score: Literal[0, 1] = Field(..., description="硬性约束匹配度,0或1。1=所有硬性约束都满足,0=任一硬性约束不满足")
-    soft_score: float = Field(..., description="软性修饰完整度,0-1的浮点数。1.0=完整保留,0.7-0.9=保留核心,0.4-0.6=泛化较大,0-0.3=大量丢失")
-    reason: str = Field(..., description="评估理由,包括:1)本质/意图是否一致 2)硬性约束是否满足 3)软性修饰保留情况 4)搜索预期")
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_instructions,
-    output_type=EvaluationFeedback,
-)
-
-@function_tool
-async def get_query_suggestions(wrapper: RunContextWrapper[RunContext], query: str):
-    """Fetch search recommendations from Xiaohongshu."""
-
-    # 1. 首次调用时,先标注问题(带需求背景)
-    if wrapper.context.question_annotation is None:
-        print("正在标注问题...")
-        annotation_result = await Runner.run(question_annotator, wrapper.context.q_with_context)
-        wrapper.context.question_annotation = str(annotation_result.final_output)
-        print(f"问题标注完成:{wrapper.context.question_annotation}")
-
-    # 2. 获取推荐词
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-    query_suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-    print(f"获取到 {len(query_suggestions) if query_suggestions else 0} 个推荐词:{query_suggestions}")
-
-    # 3. 评估推荐词(三层评分)
-    async def evaluate_single_query(q_sug: str):
-        """Evaluate a single query suggestion."""
-        eval_input = f"""
-<原始问题标注(三层)>
-{wrapper.context.question_annotation}
-</原始问题标注(三层)>
-
-<待评估的推荐query>
-{q_sug}
-</待评估的推荐query>
-
-请评估该推荐query的三个分数:
-1. essence_score: 本质/意图是否一致(0或1)
-2. hard_score: 硬性约束是否满足(0或1)
-3. soft_score: 软性修饰保留程度(0-1)
-4. reason: 详细的评估理由
-"""
-        evaluator_result = await Runner.run(evaluator, eval_input)
-        result: EvaluationFeedback = evaluator_result.final_output
-        return {
-            "query": q_sug,
-            "essence_score": result.essence_score,
-            "hard_score": result.hard_score,
-            "soft_score": result.soft_score,
-            "reason": result.reason,
-        }
-
-    # 并发执行所有评估任务
-    res = []
-    if query_suggestions:
-        res = await asyncio.gather(*[evaluate_single_query(q_sug) for q_sug in query_suggestions])
-    else:
-        res = '未返回任何推荐词'
-
-    # 记录到 RunContext
-    wrapper.context.operations_history.append({
-        "operation_type": "get_query_suggestions",
-        "timestamp": datetime.now().isoformat(),
-        "query": query,
-        "suggestions": query_suggestions,
-        "evaluations": res,
-    })
-
-    return res
-
-@function_tool
-def modify_query(wrapper: RunContextWrapper[RunContext], original_query: str, operation_type: str, new_query: str, reason: str):
-    """
-    Modify the search query with a specific operation.
-
-    Args:
-        original_query: The original query before modification
-        operation_type: Type of modification - must be one of: "简化", "扩展", "替换", "组合"
-        new_query: The modified query after applying the operation
-        reason: Detailed explanation of why this modification was made and what insight from previous suggestions led to this change
-
-    Returns:
-        A dict containing the modification record and the new query to use for next search
-    """
-    operation_types = ["简化", "扩展", "替换", "组合"]
-    if operation_type not in operation_types:
-        return {
-            "status": "error",
-            "message": f"Invalid operation_type. Must be one of: {', '.join(operation_types)}"
-        }
-
-    modification_record = {
-        "original_query": original_query,
-        "operation_type": operation_type,
-        "new_query": new_query,
-        "reason": reason,
-    }
-
-    # 记录到 RunContext
-    wrapper.context.operations_history.append({
-        "operation_type": "modify_query",
-        "timestamp": datetime.now().isoformat(),
-        "modification_type": operation_type,
-        "original_query": original_query,
-        "new_query": new_query,
-        "reason": reason,
-    })
-
-    return {
-        "status": "success",
-        "modification_record": modification_record,
-        "new_query": new_query,
-        "message": f"Query modified successfully. Use '{new_query}' for the next search."
-    }
-
-instructions = """
-你是一个专业的搜索query优化专家,擅长通过动态探索找到最符合用户搜索习惯的query。
-
-## 核心任务
-给定原始问题,通过迭代调用搜索推荐接口(get_query_suggestions),找到满足硬性要求且尽量保留软性信息的推荐query。
-
-## 重要说明
-- **你不需要自己评估query的适配性**
-- get_query_suggestions 函数会:
-  1. 首次调用时自动标注问题(三层:本质、硬性、软性)
-  2. 对每个推荐词进行三维度评估
-- 返回结果包含:
-  - **query**:推荐词
-  - **essence_score**:本质/意图匹配度(0或1),0=本质改变,1=本质一致
-  - **hard_score**:硬性约束匹配度(0或1),0=不满足约束,1=满足所有约束
-  - **soft_score**:软性修饰完整度(0-1),越高表示保留的信息越完整
-  - **reason**:详细的评估理由
-- **你的职责是分析评估结果,做出决策和策略调整**
-
-## 防止幻觉 - 关键原则
-- **严禁编造数据**:只能基于 get_query_suggestions 实际返回的结果进行分析
-- **空结果处理**:如果返回的列表为空([]),必须明确说明"未返回任何推荐词"
-- **不要猜测**:在 modify_query 的 reason 中,不能引用不存在的推荐词或评分
-- **如实记录**:每次分析都要如实反映实际返回的数据
-
-## 工作流程
-
-### 1. 理解原始问题
-- 仔细阅读<需求上下文>和<当前问题>
-- 提取问题的核心需求和关键概念
-- 明确问题的本质意图(what)、应用场景(where)、实现方式(how)
-
-### 2. 动态探索策略
-
-**第一轮尝试:**
-- 使用原始问题直接调用 get_query_suggestions(query="原始问题")
-- 第一次调用会自动标注问题(三层),后续调用会复用该标注
-- **检查返回结果**:
-  - 如果返回空列表 []:说明"该query未返回任何推荐词",需要简化或替换query
-  - 如果有推荐词:查看每个推荐词的 essence_score、hard_score、soft_score 和 reason
-- **做出判断**:是否有 essence_score=1 且 hard_score=1 且 soft_score >= 0.7 的推荐词?
-
-**后续迭代:**
-如果没有合格推荐词(或返回空列表),必须先调用 modify_query 记录修改,然后再次搜索:
-
-**工具使用流程:**
-1. **分析评估反馈**(必须基于实际返回的数据):
-   - **情况A - 返回空列表**:
-     * 在 reason 中说明:"第X轮未返回任何推荐词,可能是query过于复杂或生僻"
-     * 不能编造任何推荐词或评分
-
-   - **情况B - 有推荐词但无合格词**:
-     * **首先检查 essence_score**:
-       - 如果全是 essence_score=0:本质/意图完全不对,需要重新理解问题
-       - 如果有 essence_score=1:本质对了,继续分析
-
-     * **分析 essence_score=1 且 hard_score=1 的推荐词**:
-       - 有哪些?soft_score 是多少?
-       - 如果 soft_score 较低(<0.7),reason 中说明丢失了哪些信息?
-       - 能否通过修改query提高 soft_score?
-
-     * **如果 essence_score=1 但全是 hard_score=0**:
-       - reason 中说明了哪些硬性约束不满足?(地域、时间、对象、质量等)
-       - 需要如何调整query才能满足硬性约束?
-
-2. **决策修改策略**:基于实际评估反馈,调用 modify_query(original_query, operation_type, new_query, reason)
-   - reason 必须引用具体的 essence_score、hard_score、soft_score 和评估理由
-   - 不能编造任何数据
-
-3. 使用返回的 new_query 调用 get_query_suggestions
-
-4. 分析新的评估结果,如果仍不满足,重复步骤1-3
-
-**四种操作类型(operation_type):**
-- **简化**:删除冗余词汇,提取核心关键词(当推荐词过于发散时)
-- **扩展**:添加限定词或场景描述(当推荐词过于泛化时)
-- **替换**:使用同义词、行业术语或口语化表达(当推荐词偏离核心时)
-- **组合**:调整关键词顺序或组合方式(当推荐词结构不合理时)
-
-**每次修改的reason必须包含:**
-- 上一轮评估结果的关键发现(引用具体的 essence_score、hard_score、soft_score 和评估理由)
-- 基于评估反馈,为什么这样修改
-- 预期这次修改会带来什么改进
-
-### 3. 决策标准
-
-采用**三级评分标准**:
-
-**优先级1:本质/意图(最高优先级)**
-- **essence_score = 1**:本质一致,继续检查
-- **essence_score = 0**:本质改变,**直接放弃**
-
-**优先级2:硬性约束(必须满足)**
-- **hard_score = 1**:所有约束满足,继续检查
-- **hard_score = 0**:约束不满足,**直接放弃**
-
-**优先级3:软性修饰(越高越好)**
-- **soft_score >= 0.7**:信息保留较完整,**理想结果**
-- **0.5 <= soft_score < 0.7**:有所丢失但可接受,**备选结果**
-- **soft_score < 0.5**:丢失过多,继续优化
-
-**采纳标准:**
-- **最优**:essence=1 且 hard=1 且 soft >= 0.7
-- **可接受**:essence=1 且 hard=1 且 soft >= 0.5(多次尝试后)
-- **不可接受**:essence=0 或 hard=0(无论soft多高)
-
-### 4. 迭代终止条件
-- **成功终止**:找到 essence=1 且 hard=1 且 soft >= 0.7 的推荐query
-- **可接受终止**:5轮后找到 essence=1 且 hard=1 且 soft >= 0.5 的推荐query
-- **失败终止**:最多5轮
-- **无推荐词**:返回空列表或错误
-
-### 5. 输出要求
-
-**成功找到合格query时:**
-```
-原始问题:[原问题]
-优化后的query:[最终推荐query]
-本质匹配度:[essence_score] (1=本质一致)
-硬性约束匹配度:[hard_score] (1=所有约束满足)
-软性修饰完整度:[soft_score] (0-1)
-评估理由:[简要说明]
-```
-
-**未找到合格query时:**
-```
-原始问题:[原问题]
-结果:未找到合格推荐query
-原因:[本质不符 / 硬性约束不满足 / 软性信息丢失过多]
-最接近的推荐词:[如果有essence=1且hard=1的词,列出soft最高的]
-建议:[简要建议]
-```
-
-## 注意事项
-- **第一轮必须使用原始问题**:直接调用 get_query_suggestions(query="原始问题")
-  - 第一次调用会自动标注问题(三层),打印出标注结果
-  - 后续调用会复用该标注进行评估
-
-- **后续修改必须调用 modify_query**:不能直接用新query调用 get_query_suggestions
-
-- **重点关注评估结果**:每次都要仔细分析返回的三个分数
-  - **essence_score=0 直接放弃**,本质不对
-  - **hard_score=0 也直接放弃**,约束不满足
-  - **优先关注 essence=1 且 hard=1 的推荐词**,分析如何提升 soft_score
-
-- **基于数据决策**:修改策略必须基于评估反馈
-  - 引用具体的 essence_score、hard_score、soft_score
-  - 引用 reason 中的关键发现
-
-- **采纳标准明确**:
-  - **最优**:essence=1 且 hard=1 且 soft >= 0.7,立即采纳
-  - **可接受**:essence=1 且 hard=1 且 soft >= 0.5,多次尝试后可采纳
-  - **不可接受**:essence=0 或 hard=0,无论soft多高都不能用
-
-- **严禁编造数据**:
-  * 如果返回空列表,必须明确说明"未返回任何推荐词"
-  * 不能引用不存在的推荐词、分数或评估理由
-  * 每次 modify_query 的 reason 必须基于上一轮实际返回的结果
-""".strip()
-
-
-
-
-async def main(input_dir: str):
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]  # 去掉 .py 后缀
-
-    # 日志保存到输入目录的 output/版本/时间戳 目录下
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-    agent = Agent[RunContext](
-        name="Query Optimization Agent",
-        instructions=instructions,
-        tools=[get_query_suggestions, modify_query],
-    )
-    result = await Runner.run(agent, input=q_with_context,  context = run_context,)
-    print(result.final_output)
-
-    # 保存最终输出到 RunContext
-    run_context.final_output = str(result.final_output)
-
-    # 保存 RunContext 到 log_dir
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    args = parser.parse_args()
-
-    asyncio.run(main(args.input_dir))

+ 0 - 726
sug_v5.py

@@ -1,726 +0,0 @@
-import asyncio
-import json
-import os
-import argparse
-from datetime import datetime
-
-from agents import Agent, Runner
-from lib.my_trace import set_trace
-from typing import Literal
-from pydantic import BaseModel, Field
-
-from lib.utils import read_file_as_string
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-    question_annotation: str | None = Field(default=None, description="问题的标注结果")
-    operations_history: list[dict] = Field(default_factory=list, description="记录所有操作的历史")
-    final_output: str | None = Field(default=None, description="最终输出结果")
-
-
-# ============================================================================
-# Agent 1: 问题标注专家
-# ============================================================================
-question_annotation_instructions = """
-你是搜索需求分析专家。给定问题(含需求背景),在原文上标注三层:本质、硬性、软性。
-
-## 判断标准
-
-**[本质]** - 问题的核心意图
-- 如何获取、教程、推荐、作品、测评等
-
-**[硬]** - 客观事实性约束(可明确验证、非主观判断)
-- 能明确区分类别的:地域、时间、对象、工具、操作类型
-- 特征:改变后得到完全不同类别的结果
-
-**[软]** - 主观判断性修饰(因人而异、程度性的)
-- 需要主观评价的:质量、速度、美观、特色、程度
-- 特征:改变后仍是同类结果,只是满足程度不同
-
-## 输出格式
-
-词语[本质-描述]、词语[硬-描述]、词语[软-描述]
-
-## 注意
-- 只输出标注后的字符串
-- 结合需求背景判断意图
-""".strip()
-
-question_annotator = Agent[None](
-    name="问题标注专家",
-    instructions=question_annotation_instructions,
-)
-
-
-# ============================================================================
-# Agent 2: 评估专家
-# ============================================================================
-eval_instructions = """
-你是搜索query评估专家。给定原始问题标注(三层)和推荐query,评估三个分数。
-
-## 评估目标
-
-用这个推荐query搜索,能否找到满足原始需求的内容?
-
-## 三层评分
-
-### 1. essence_score(本质/意图)= 0 或 1
-
-推荐query的本质/意图是否与原问题一致?
-
-**原问题标注中的[本质-XXX]对应推荐词要求:**
-- 找方法/如何获取 → 推荐词**必须明确包含方法/途径类词汇**
-  - ✅ "...网站推荐"、"如何获取..."、"...获取途径"、"...方法"
-  - ❌ "...下载"、"...素材"(直接找内容,不是找方法)
-- 教程/学习 → 推荐词应该是教程/教学
-- 作品/欣赏 → 推荐词应该是作品展示
-- 工具/推荐 → 推荐词应该是工具推荐
-
-**评分原则:**
-- 1 = 本质一致,推荐词**明确表达**相同意图
-- 0 = 本质改变或**不够明确**(宁可严格,不可放松)
-
-### 2. hard_score(硬性约束)= 0 或 1
-
-在本质一致的前提下,是否满足所有硬性约束?
-
-**原问题标注中的[硬-XXX]:**地域、时间、对象、质量、工具等
-
-**评分:**
-- 1 = 所有硬性约束都满足
-- 0 = 任一硬性约束不满足
-
-### 3. soft_score(软性修饰)= 0-1
-
-软性修饰词保留了多少?
-
-**评分参考:**
-- 1.0 = 完整保留
-- 0.7-0.9 = 保留核心
-- 0.4-0.6 = 部分丢失
-- 0-0.3 = 大量丢失
-
-## 注意
-
-- essence=0 直接拒绝,不管hard/soft多高
-- essence=1, hard=0 也要拒绝
-- essence=1, hard=1 才看soft_score
-""".strip()
-
-class EvaluationFeedback(BaseModel):
-    """评估反馈模型 - 三层评分"""
-    essence_score: Literal[0, 1] = Field(..., description="本质/意图匹配度,0或1")
-    hard_score: Literal[0, 1] = Field(..., description="硬性约束匹配度,0或1")
-    soft_score: float = Field(..., description="软性修饰完整度,0-1")
-    reason: str = Field(..., description="评估理由")
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_instructions,
-    output_type=EvaluationFeedback,
-)
-
-
-# ============================================================================
-# Agent 3: 修改策略生成专家
-# ============================================================================
-strategy_instructions = """
-你是query修改策略专家。**模拟人在搜索引擎中的真实搜索行为**,基于反馈动态调整query。
-
-## 核心思路:搜索是探索过程,不是直达过程
-
-**关键认知:**
-1. **中间query不需要满足原始需求** - 它是探索工具,可以偏离原需求
-2. **推荐词是最宝贵的反馈信号** - 告诉你系统理解成什么了,有什么内容
-3. **每一步query都有明确的探索目的** - 不是盲目改词,而是试探和引导
-4. **最终目标:找到满足需求的推荐词** - 不是让query本身满足需求
-
-## 人的真实搜索过程
-
-**搜索的本质**:通过多步探索,利用推荐词作为桥梁,逐步引导系统
-
-**典型模式**:
-
-第1步:直接尝试
-- 目的:看系统能否直接理解
-- 结果:空列表或essence=0
-- essence=0的推荐词:告诉你系统理解成什么了
-
-第2步:降低要求,简化query
-- 目的:让系统有响应,看它在基础层面有什么
-- 推荐词虽然essence=0,但揭示了系统在某个主题有内容
-- **关键**:选一个最有潜力的推荐词
-
-第3步:基于推荐词,往目标方向引导
-- 目的:利用推荐词作为桥梁,加上目标方向的词
-- 推荐词还是essence=0,但主题在变化(接近目标)
-- **渐进式**:不求一步到位,每步都有进展
-
-第4步:继续引导或换角度
-- 如果推荐词主题不变 → 换角度
-- 如果推荐词主题在接近 → 继续引导
-
-最终:找到essence=1的推荐词
-
-**关键原则**:
-1. essence_score是评估推荐词的,不是评估中间query的
-2. essence=0的推荐词也有价值,它揭示了系统的理解方向
-3. 每一步都有明确的探索目的,看目的是否达成
-4. 通过推荐词的主题变化,判断是否在接近目标
-
-## 输入信息
-- 原始问题标注(三层):本质、硬性约束、软性修饰
-- 历史尝试记录:所有轮次的query、推荐词、评估结果
-- 当前query和推荐词评估
-
-## 分析步骤
-
-### 第一步:理解当前推荐词的信号
-**核心问题:推荐词告诉我什么信息?**
-
-**重要提醒:essence_score是评估推荐词是否满足原始需求的最终目标**
-- essence_score=1: 推荐词满足原需求的本质
-- essence_score=0: 推荐词不满足原需求的本质
-- **但中间query的目的可能不是满足原需求**,所以essence_score只是参考
-
-1. **系统理解层面**(看推荐词的主题):
-   - 空列表 → 系统完全不理解当前query
-   - 有推荐词 → 系统理解成了什么主题?
-     - 旅游?教程?素材?工具?品种介绍?
-     - 这些主题是否有助于往目标方向引导?
-
-2. **内容可用性层面**(看推荐词的价值):
-   - **即使推荐词essence=0,也可能是很好的探索起点**
-   - 例如:推荐词"川西旅游攻略"虽然essence=0,但揭示了系统认识"川西"
-   - 哪些推荐词最有潜力作为下一步的桥梁?
-
-3. **探索目的验证**:
-   - 当前query的探索目的是什么?达到了吗?
-   - 例如:目的是"看系统对川西有什么" → 达到了(有推荐词)
-   - 下一步要验证/探索什么?
-
-### 第二步:回顾历史,识别规律
-- 哪些query让系统理解方向变化了?(从"旅游"变成"摄影")
-- 哪些方向是死路?(多次essence=0且推荐词主题不变)
-- **是否有渐进的改善?**(推荐词越来越接近目标)
-
-### 第三步:选择策略类型(带着明确的探索目的)
-
-**refine_current(微调当前query)**
-- 适用:推荐词方向对了,需要微调让它更精确
-- 探索目的:在正确方向上精细化
-- 动作:加词/减词/换词/调整顺序
-
-**use_recommendation(选推荐词作为新起点)** ⭐ 最重要策略
-- 适用:推荐词虽然essence=0,但**揭示了系统在这个方向有内容**
-- 探索目的:利用推荐词这个客观信号,引导系统往目标方向
-- **核心思维**:推荐词是系统给你的提示,告诉你"我有这个"
-- 动作:
-  - 选一个最有潜力的推荐词作为base_query
-  - 在它基础上加目标方向的词
-  - **这个新query可能不满足原需求,但目的是探索和引导**
-
-**change_approach(换完全不同的角度)**
-- 适用:当前方向是死路(多次尝试推荐词主题不变)
-- 探索目的:跳出当前框架,从另一个角度切入
-- 动作:换一种完全不同的表述方式
-
-**relax_constraints(放宽约束)**
-- 适用:query太复杂,系统不理解(返回空列表)
-- 探索目的:先让系统有响应,看它在最基础层面有什么
-- 动作:去掉限定词,保留核心概念
-
-## 输出要求
-
-### 1. reasoning(推理过程)
-必须包含三部分,**重点写探索目的**:
-
-- **当前推荐词信号分析**:
-  - 系统理解成什么主题了?(旅游?教程?素材?工具?品种?)
-  - 推荐词揭示了什么信息?(系统在哪个方向有内容)
-  - **不要只看essence_score**:
-    - essence_score=0不代表推荐词没用
-    - 关键看推荐词的主题是否有助于引导
-  - 哪个推荐词最有潜力作为下一步的桥梁?
-
-- **历史尝试与趋势**:
-  - 系统理解的主题变化:从"品种介绍"→"旅游"→"摄影"
-  - 是否在逐步接近目标?还是原地打转?
-
-- **下一步策略与探索目的**:
-  - **这一步query的探索目的是什么?**
-    - 验证系统对某个词的理解?
-    - 往某个方向引导?
-    - 利用推荐词作为桥梁?
-  - 为什么选这个base_query?
-  - 为什么这样修改?
-  - **重要**:不要纠结"这个query不满足原需求",关键是它能否达成探索目的
-
-### 2. strategy_type
-从4种策略中选择:refine_current, use_recommendation, change_approach, relax_constraints
-
-### 3. base_query
-**关键**:可以选择历史中的query,也可以选择历史推荐词
-- 如果选历史query:base_query_source = "history_query"
-- 如果选历史推荐词:base_query_source = "history_recommendation"
-
-### 4. base_query_source
-说明base_query的来源
-
-### 5. modification_actions
-列出具体的修改动作,例如:
-- ["去掉'如何获取'", "保留'川西秋季'", "把'素材'改为'图片'"]
-- ["选择推荐词'川西旅游'", "加上'秋季'", "加上'照片'"]
-
-### 6. new_query
-最终的新query
-
-## 重要原则
-
-1. **推荐词是最宝贵的反馈** - 充分利用推荐词这个客观信号
-   - 即使essence=0的推荐词,也揭示了系统在这个方向有什么
-   - **优先考虑use_recommendation策略** - 选一个推荐词作为起点
-
-2. **中间query可以偏离原需求** - 每一步都有明确的探索目的
-   - 不要纠结"这个query不满足原需求"
-   - 关键是:这个query能不能帮你往正确方向引导系统
-
-3. **识别死胡同,及时换方向**
-   - 如果多次尝试推荐词主题不变 → 换方向
-   - 如果推荐词越来越偏 → 回退到之前的某个好的起点
-
-4. **保持推理简洁** - 抓住关键信息
-   - 明确说出探索目的
-   - 不要重复啰嗦
-""".strip()
-
-class ModificationStrategy(BaseModel):
-    """修改策略模型 - 模拟人的搜索调整过程"""
-    reasoning: str = Field(..., description="推理过程:1)当前推荐词分析:系统理解成什么了?2)历史尝试总结:哪些方向有效/无效?3)下一步策略:为什么这样调整?")
-
-    strategy_type: Literal[
-        "refine_current",      # 微调当前query(加词/减词/换词/换顺序)
-        "use_recommendation",  # 选择推荐词作为新起点,在它基础上修改
-        "change_approach",     # 换完全不同的表述角度
-        "relax_constraints"    # 放宽约束,去掉部分限定词
-    ] = Field(..., description="策略类型")
-
-    base_query: str = Field(..., description="基础query,可以是:1)历史中的query 2)历史推荐词中的某一个")
-    base_query_source: Literal["history_query", "history_recommendation"] = Field(..., description="base_query的来源")
-
-    modification_actions: list[str] = Field(..., description="具体修改动作的描述,如:['去掉\"如何获取\"', '保留核心词\"川西秋季\"', '把\"素材\"改为\"图片\"']")
-
-    new_query: str = Field(..., description="修改后的新query")
-
-strategy_generator = Agent[None](
-    name="策略生成专家",
-    instructions=strategy_instructions,
-    output_type=ModificationStrategy,
-)
-
-
-# ============================================================================
-# 核心函数
-# ============================================================================
-
-async def annotate_question(q_with_context: str) -> str:
-    """标注问题(三层)"""
-    print("\n正在标注问题...")
-    result = await Runner.run(question_annotator, q_with_context)
-    annotation = str(result.final_output)
-    print(f"问题标注完成:{annotation}")
-    return annotation
-
-
-async def get_suggestions_with_eval(query: str, annotation: str, context: RunContext) -> list[dict]:
-    """获取推荐词并评估"""
-    print(f"\n正在获取推荐词:{query}")
-
-    # 1. 调用小红书API
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-    query_suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-    print(f"获取到 {len(query_suggestions) if query_suggestions else 0} 个推荐词:{query_suggestions}")
-
-    if not query_suggestions:
-        # 记录到历史
-        context.operations_history.append({
-            "operation_type": "get_query_suggestions",
-            "timestamp": datetime.now().isoformat(),
-            "query": query,
-            "suggestions": [],
-            "evaluations": "未返回任何推荐词",
-        })
-        return []
-
-    # 2. 并发评估所有推荐词
-    async def evaluate_single_query(q_sug: str):
-        eval_input = f"""
-<原始问题标注(三层)>
-{annotation}
-</原始问题标注(三层)>
-
-<待评估的推荐query>
-{q_sug}
-</待评估的推荐query>
-
-请评估该推荐query的三个分数:
-1. essence_score: 本质/意图是否一致(0或1)
-2. hard_score: 硬性约束是否满足(0或1)
-3. soft_score: 软性修饰保留程度(0-1)
-4. reason: 详细的评估理由
-"""
-        evaluator_result = await Runner.run(evaluator, eval_input)
-        result: EvaluationFeedback = evaluator_result.final_output
-        return {
-            "query": q_sug,
-            "essence_score": result.essence_score,
-            "hard_score": result.hard_score,
-            "soft_score": result.soft_score,
-            "reason": result.reason,
-        }
-
-    evaluations = await asyncio.gather(*[evaluate_single_query(q_sug) for q_sug in query_suggestions])
-
-    # 3. 记录到历史
-    context.operations_history.append({
-        "operation_type": "get_query_suggestions",
-        "timestamp": datetime.now().isoformat(),
-        "query": query,
-        "suggestions": query_suggestions,
-        "evaluations": evaluations,
-    })
-
-    return evaluations
-
-
-async def generate_modification_strategy(
-    current_query: str,
-    evaluations: list[dict],
-    annotation: str,
-    context: RunContext
-) -> ModificationStrategy:
-    """生成修改策略"""
-    print("\n正在生成修改策略...")
-
-    # 整理历史尝试记录 - 完整保留推荐词和评估结果
-    history_records = []
-    round_num = 0
-
-    for op in context.operations_history:
-        if op["operation_type"] == "get_query_suggestions":
-            round_num += 1
-            record = {
-                "round": round_num,
-                "query": op["query"],
-                "suggestions": op["suggestions"],
-                "evaluations": op["evaluations"]
-            }
-            history_records.append(record)
-        elif op["operation_type"] == "modify_query":
-            # 修改操作也记录,但不增加轮数
-            history_records.append({
-                "operation": "modify_query",
-                "strategy_type": op.get("strategy_type", op.get("modification_type")),  # 兼容旧字段
-                "base_query": op.get("base_query"),
-                "base_query_source": op.get("base_query_source"),
-                "modification_actions": op.get("modification_actions", []),
-                "original_query": op["original_query"],
-                "new_query": op["new_query"],
-                "reasoning": op["reasoning"]
-            })
-
-    # 格式化历史记录为JSON
-    history_json = json.dumps(history_records, ensure_ascii=False, indent=2)
-
-    strategy_input = f"""
-<原始问题标注(三层)>
-{annotation}
-</原始问题标注(三层)>
-
-<历史尝试记录(完整)>
-{history_json}
-</历史尝试记录(完整)>
-
-<当前query>
-{current_query}
-</当前query>
-
-<当前轮推荐词评估结果>
-{json.dumps(evaluations, ensure_ascii=False, indent=2) if evaluations else "空列表"}
-</当前轮推荐词评估结果>
-
-请基于所有历史尝试和当前评估结果,生成下一步的query修改策略。
-
-重点分析:
-
-1. **当前推荐词的信号**:
-   - 系统理解成什么主题了?(旅游?教程?素材?工具?品种?)
-   - 推荐词揭示了什么信息?系统在哪个方向有内容?
-   - **不要只看essence_score**:essence=0的推荐词也可能是好的探索起点
-   - 哪个推荐词最有潜力作为下一步的桥梁?
-
-2. **历史趋势分析**:
-   - 推荐词的主题变化:从"品种介绍"→"旅游"→"摄影"?
-   - 是否在逐步接近目标?还是原地打转(主题不变)?
-   - 哪些query让系统理解方向改变了?
-
-3. **确定探索目的**:
-   - 下一步query的探索目的是什么?
-     * 验证系统对某个词的理解?
-     * 往某个方向引导系统?
-     * 利用推荐词作为桥梁?
-   - **记住**:中间query不需要满足原需求,关键是达成探索目的
-"""
-    result = await Runner.run(strategy_generator, strategy_input)
-    strategy: ModificationStrategy = result.final_output
-    return strategy
-
-
-def find_qualified_queries(evaluations: list[dict], min_soft_score: float = 0.7) -> list[dict]:
-    """查找所有合格的query,按soft_score降序排列"""
-    qualified = [
-        e for e in evaluations
-        if e['essence_score'] == 1
-        and e['hard_score'] == 1
-        and e['soft_score'] >= min_soft_score
-    ]
-    # 按soft_score降序排列
-    return sorted(qualified, key=lambda x: x['soft_score'], reverse=True)
-
-
-# ============================================================================
-# 主流程(代码控制)
-# ============================================================================
-
-async def optimize_query(context: RunContext, max_rounds: int = 20) -> dict:
-    """
-    主优化流程 - 由代码控制
-
-    Args:
-        context: 运行上下文
-        max_rounds: 最大迭代轮数,默认20
-
-    返回格式:
-    {
-        "success": True/False,
-        "result": {...} or None,
-        "message": "..."
-    }
-    """
-    # 1. 标注问题(仅一次)
-    annotation = await annotate_question(context.q_with_context)
-    context.question_annotation = annotation
-
-    # 2. 迭代优化
-    current_query = context.q
-
-    for round_num in range(1, max_rounds + 1):
-        print(f"\n{'='*60}")
-        print(f"第 {round_num} 轮:{'使用原始问题' if round_num == 1 else '使用修改后的query'}")
-        print(f"当前query: {current_query}")
-        print(f"{'='*60}")
-
-        # 获取推荐词并评估
-        evaluations = await get_suggestions_with_eval(current_query, annotation, context)
-
-        if evaluations:
-            # 检查是否找到合格query
-            qualified_queries = find_qualified_queries(evaluations, min_soft_score=0.7)
-            if qualified_queries:
-                return {
-                    "success": True,
-                    "results": qualified_queries,
-                    "message": f"第{round_num}轮找到{len(qualified_queries)}个合格query"
-                }
-
-        # 如果是最后一轮,不再生成策略
-        if round_num == max_rounds:
-            break
-
-        # 生成修改策略
-        print(f"\n--- 生成修改策略 ---")
-        strategy = await generate_modification_strategy(current_query, evaluations, annotation, context)
-
-        print(f"\n修改策略:")
-        print(f"  推理过程:{strategy.reasoning}")
-        print(f"  策略类型:{strategy.strategy_type}")
-        print(f"  基础query:{strategy.base_query} (来源: {strategy.base_query_source})")
-        print(f"  修改动作:{', '.join(strategy.modification_actions)}")
-        print(f"  新query:{strategy.new_query}")
-
-        # 记录修改
-        context.operations_history.append({
-            "operation_type": "modify_query",
-            "timestamp": datetime.now().isoformat(),
-            "reasoning": strategy.reasoning,
-            "strategy_type": strategy.strategy_type,
-            "base_query": strategy.base_query,
-            "base_query_source": strategy.base_query_source,
-            "modification_actions": strategy.modification_actions,
-            "original_query": current_query,
-            "new_query": strategy.new_query,
-        })
-
-        # 更新当前query
-        current_query = strategy.new_query
-
-    # 所有轮次后仍未找到,降低标准查找
-    print(f"\n{'='*60}")
-    print(f"{max_rounds}轮后未找到最优query,降低标准(soft_score >= 0.5)")
-    print(f"{'='*60}")
-
-    acceptable_queries = find_qualified_queries(evaluations, min_soft_score=0.5)
-    if acceptable_queries:
-        return {
-            "success": True,
-            "results": acceptable_queries,
-            "message": f"{max_rounds}轮后找到{len(acceptable_queries)}个可接受query(soft_score >= 0.5)"
-        }
-
-    # 完全失败:找出最接近的
-    essence_hard_ok = [
-        e for e in evaluations
-        if e['essence_score'] == 1 and e['hard_score'] == 1
-    ]
-    if essence_hard_ok:
-        # 返回所有满足essence和hard的,按soft_score降序
-        closest_queries = sorted(essence_hard_ok, key=lambda x: x['soft_score'], reverse=True)
-        return {
-            "success": False,
-            "results": closest_queries,
-            "message": f"未找到合格query,但有{len(closest_queries)}个接近的推荐词(essence=1, hard=1)"
-        }
-
-    return {
-        "success": False,
-        "results": [],
-        "message": "未找到任何满足本质和硬性约束的推荐词"
-    }
-
-
-# ============================================================================
-# 输出格式化
-# ============================================================================
-
-def format_output(optimization_result: dict, context: RunContext) -> str:
-    """格式化输出结果"""
-    results = optimization_result.get("results", [])
-
-    if optimization_result["success"] and results:
-        output = f"原始问题:{context.q}\n"
-        output += f"状态:{optimization_result['message']}\n\n"
-        output += "合格的推荐query(按soft_score降序):\n"
-        for i, result in enumerate(results, 1):
-            output += f"\n{i}. {result['query']}\n"
-            output += f"   - 本质匹配度:{result['essence_score']} (1=本质一致)\n"
-            output += f"   - 硬性约束匹配度:{result['hard_score']} (1=所有约束满足)\n"
-            output += f"   - 软性修饰完整度:{result['soft_score']:.2f} (0-1)\n"
-            output += f"   - 评估理由:{result['reason']}\n"
-        return output.strip()
-    else:
-        output = f"原始问题:{context.q}\n"
-        output += f"结果:未找到合格推荐query\n"
-        output += f"原因:{optimization_result['message']}\n"
-
-        if results:
-            output += "\n最接近的推荐词(按soft_score降序):\n"
-            for i, result in enumerate(results[:3], 1):  # 只显示前3个
-                output += f"\n{i}. {result['query']}\n"
-                output += f"   - essence_score: {result['essence_score']}\n"
-                output += f"   - hard_score: {result['hard_score']}\n"
-                output += f"   - soft_score: {result['soft_score']:.2f}\n"
-                output += f"   - reason: {result['reason']}\n"
-
-        output += "\n建议:尝试简化问题或调整需求描述"
-        return output.strip()
-
-
-# ============================================================================
-# 主函数
-# ============================================================================
-
-async def main(input_dir: str, max_rounds: int = 20):
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]
-
-    # 日志保存目录
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-
-    # 执行优化流程(代码控制)
-    optimization_result = await optimize_query(run_context, max_rounds=max_rounds)
-
-    # 格式化输出
-    final_output = format_output(optimization_result, run_context)
-    print(f"\n{'='*60}")
-    print("最终结果")
-    print(f"{'='*60}")
-    print(final_output)
-
-    # 保存结果
-    run_context.final_output = final_output
-
-    # 保存 RunContext 到 log_dir
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    parser.add_argument(
-        "--max-rounds",
-        type=int,
-        default=20,
-        help="最大迭代轮数,默认: 20"
-    )
-    args = parser.parse_args()
-
-    asyncio.run(main(args.input_dir, max_rounds=args.max_rounds))

+ 0 - 728
sug_v5_0.py

@@ -1,728 +0,0 @@
-import asyncio
-import json
-import os
-import argparse
-from datetime import datetime
-
-from agents import Agent, Runner
-from lib.my_trace import set_trace
-from typing import Literal
-from pydantic import BaseModel, Field
-
-from lib.utils import read_file_as_string
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-    question_annotation: str | None = Field(default=None, description="问题的标注结果")
-    operations_history: list[dict] = Field(default_factory=list, description="记录所有操作的历史")
-    optimization_result: dict | None = Field(default=None, description="最终优化结果对象")
-    final_output: str | None = Field(default=None, description="最终输出结果(格式化文本)")
-
-
-# ============================================================================
-# Agent 1: 问题标注专家
-# ============================================================================
-question_annotation_instructions = """
-你是搜索需求分析专家。给定问题(含需求背景),在原文上标注三层:本质、硬性、软性。
-
-## 判断标准
-
-**[本质]** - 问题的核心意图
-- 如何获取、教程、推荐、作品、测评等
-
-**[硬]** - 客观事实性约束(可明确验证、非主观判断)
-- 能明确区分类别的:地域、时间、对象、工具、操作类型
-- 特征:改变后得到完全不同类别的结果
-
-**[软]** - 主观判断性修饰(因人而异、程度性的)
-- 需要主观评价的:质量、速度、美观、特色、程度
-- 特征:改变后仍是同类结果,只是满足程度不同
-
-## 输出格式
-
-词语[本质-描述]、词语[硬-描述]、词语[软-描述]
-
-## 注意
-- 只输出标注后的字符串
-- 结合需求背景判断意图
-""".strip()
-
-question_annotator = Agent[None](
-    name="问题标注专家",
-    instructions=question_annotation_instructions,
-)
-
-
-# ============================================================================
-# Agent 2: 评估专家
-# ============================================================================
-eval_instructions = """
-你是搜索query评估专家。给定原始问题标注(三层)和推荐query,评估三个分数。
-
-## 评估目标
-
-用这个推荐query搜索,能否找到满足原始需求的内容?
-
-## 三层评分
-
-### 1. essence_score(本质/意图)= 0 或 1
-
-推荐query的本质/意图是否与原问题一致?
-
-**原问题标注中的[本质-XXX]对应推荐词要求:**
-- 找方法/如何获取 → 推荐词**必须明确包含方法/途径类词汇**
-  - ✅ "...网站推荐"、"如何获取..."、"...获取途径"、"...方法"
-  - ❌ "...下载"、"...素材"(直接找内容,不是找方法)
-- 教程/学习 → 推荐词应该是教程/教学
-- 作品/欣赏 → 推荐词应该是作品展示
-- 工具/推荐 → 推荐词应该是工具推荐
-
-**评分原则:**
-- 1 = 本质一致,推荐词**明确表达**相同意图
-- 0 = 本质改变或**不够明确**(宁可严格,不可放松)
-
-### 2. hard_score(硬性约束)= 0 或 1
-
-在本质一致的前提下,是否满足所有硬性约束?
-
-**原问题标注中的[硬-XXX]:**地域、时间、对象、质量、工具等
-
-**评分:**
-- 1 = 所有硬性约束都满足
-- 0 = 任一硬性约束不满足
-
-### 3. soft_score(软性修饰)= 0-1
-
-软性修饰词保留了多少?
-
-**评分参考:**
-- 1.0 = 完整保留
-- 0.7-0.9 = 保留核心
-- 0.4-0.6 = 部分丢失
-- 0-0.3 = 大量丢失
-
-## 注意
-
-- essence=0 直接拒绝,不管hard/soft多高
-- essence=1, hard=0 也要拒绝
-- essence=1, hard=1 才看soft_score
-""".strip()
-
-class EvaluationFeedback(BaseModel):
-    """评估反馈模型 - 三层评分"""
-    essence_score: Literal[0, 1] = Field(..., description="本质/意图匹配度,0或1")
-    hard_score: Literal[0, 1] = Field(..., description="硬性约束匹配度,0或1")
-    soft_score: float = Field(..., description="软性修饰完整度,0-1")
-    reason: str = Field(..., description="评估理由")
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_instructions,
-    output_type=EvaluationFeedback,
-)
-
-
-# ============================================================================
-# Agent 3: 修改策略生成专家
-# ============================================================================
-strategy_instructions = """
-你是query修改策略专家。**模拟人在搜索引擎中的真实搜索行为**,基于反馈动态调整query。
-
-## 核心思路:搜索是探索过程,不是直达过程
-
-**关键认知:**
-1. **中间query不需要满足原始需求** - 它是探索工具,可以偏离原需求
-2. **推荐词是最宝贵的反馈信号** - 告诉你系统理解成什么了,有什么内容
-3. **每一步query都有明确的探索目的** - 不是盲目改词,而是试探和引导
-4. **最终目标:找到满足需求的推荐词** - 不是让query本身满足需求
-
-## 人的真实搜索过程
-
-**搜索的本质**:通过多步探索,利用推荐词作为桥梁,逐步引导系统
-
-**典型模式**:
-
-第1步:直接尝试
-- 目的:看系统能否直接理解
-- 结果:空列表或essence=0
-- essence=0的推荐词:告诉你系统理解成什么了
-
-第2步:降低要求,简化query
-- 目的:让系统有响应,看它在基础层面有什么
-- 推荐词虽然essence=0,但揭示了系统在某个主题有内容
-- **关键**:选一个最有潜力的推荐词
-
-第3步:基于推荐词,往目标方向引导
-- 目的:利用推荐词作为桥梁,加上目标方向的词
-- 推荐词还是essence=0,但主题在变化(接近目标)
-- **渐进式**:不求一步到位,每步都有进展
-
-第4步:继续引导或换角度
-- 如果推荐词主题不变 → 换角度
-- 如果推荐词主题在接近 → 继续引导
-
-最终:找到essence=1的推荐词
-
-**关键原则**:
-1. essence_score是评估推荐词的,不是评估中间query的
-2. essence=0的推荐词也有价值,它揭示了系统的理解方向
-3. 每一步都有明确的探索目的,看目的是否达成
-4. 通过推荐词的主题变化,判断是否在接近目标
-
-## 输入信息
-- 原始问题标注(三层):本质、硬性约束、软性修饰
-- 历史尝试记录:所有轮次的query、推荐词、评估结果
-- 当前query和推荐词评估
-
-## 分析步骤
-
-### 第一步:理解当前推荐词的信号
-**核心问题:推荐词告诉我什么信息?**
-
-**重要提醒:essence_score是评估推荐词是否满足原始需求的最终目标**
-- essence_score=1: 推荐词满足原需求的本质
-- essence_score=0: 推荐词不满足原需求的本质
-- **但中间query的目的可能不是满足原需求**,所以essence_score只是参考
-
-1. **系统理解层面**(看推荐词的主题):
-   - 空列表 → 系统完全不理解当前query
-   - 有推荐词 → 系统理解成了什么主题?
-     - 旅游?教程?素材?工具?品种介绍?
-     - 这些主题是否有助于往目标方向引导?
-
-2. **内容可用性层面**(看推荐词的价值):
-   - **即使推荐词essence=0,也可能是很好的探索起点**
-   - 例如:推荐词"川西旅游攻略"虽然essence=0,但揭示了系统认识"川西"
-   - 哪些推荐词最有潜力作为下一步的桥梁?
-
-3. **探索目的验证**:
-   - 当前query的探索目的是什么?达到了吗?
-   - 例如:目的是"看系统对川西有什么" → 达到了(有推荐词)
-   - 下一步要验证/探索什么?
-
-### 第二步:回顾历史,识别规律
-- 哪些query让系统理解方向变化了?(从"旅游"变成"摄影")
-- 哪些方向是死路?(多次essence=0且推荐词主题不变)
-- **是否有渐进的改善?**(推荐词越来越接近目标)
-
-### 第三步:选择策略类型(带着明确的探索目的)
-
-**refine_current(微调当前query)**
-- 适用:推荐词方向对了,需要微调让它更精确
-- 探索目的:在正确方向上精细化
-- 动作:加词/减词/换词/调整顺序
-
-**use_recommendation(选推荐词作为新起点)** ⭐ 最重要策略
-- 适用:推荐词虽然essence=0,但**揭示了系统在这个方向有内容**
-- 探索目的:利用推荐词这个客观信号,引导系统往目标方向
-- **核心思维**:推荐词是系统给你的提示,告诉你"我有这个"
-- 动作:
-  - 选一个最有潜力的推荐词作为base_query
-  - 在它基础上加目标方向的词
-  - **这个新query可能不满足原需求,但目的是探索和引导**
-
-**change_approach(换完全不同的角度)**
-- 适用:当前方向是死路(多次尝试推荐词主题不变)
-- 探索目的:跳出当前框架,从另一个角度切入
-- 动作:换一种完全不同的表述方式
-
-**relax_constraints(放宽约束)**
-- 适用:query太复杂,系统不理解(返回空列表)
-- 探索目的:先让系统有响应,看它在最基础层面有什么
-- 动作:去掉限定词,保留核心概念
-
-## 输出要求
-
-### 1. reasoning(推理过程)
-必须包含三部分,**重点写探索目的**:
-
-- **当前推荐词信号分析**:
-  - 系统理解成什么主题了?(旅游?教程?素材?工具?品种?)
-  - 推荐词揭示了什么信息?(系统在哪个方向有内容)
-  - **不要只看essence_score**:
-    - essence_score=0不代表推荐词没用
-    - 关键看推荐词的主题是否有助于引导
-  - 哪个推荐词最有潜力作为下一步的桥梁?
-
-- **历史尝试与趋势**:
-  - 系统理解的主题变化:从"品种介绍"→"旅游"→"摄影"
-  - 是否在逐步接近目标?还是原地打转?
-
-- **下一步策略与探索目的**:
-  - **这一步query的探索目的是什么?**
-    - 验证系统对某个词的理解?
-    - 往某个方向引导?
-    - 利用推荐词作为桥梁?
-  - 为什么选这个base_query?
-  - 为什么这样修改?
-  - **重要**:不要纠结"这个query不满足原需求",关键是它能否达成探索目的
-
-### 2. strategy_type
-从4种策略中选择:refine_current, use_recommendation, change_approach, relax_constraints
-
-### 3. base_query
-**关键**:可以选择历史中的query,也可以选择历史推荐词
-- 如果选历史query:base_query_source = "history_query"
-- 如果选历史推荐词:base_query_source = "history_recommendation"
-
-### 4. base_query_source
-说明base_query的来源
-
-### 5. modification_actions
-列出具体的修改动作,例如:
-- ["去掉'如何获取'", "保留'川西秋季'", "把'素材'改为'图片'"]
-- ["选择推荐词'川西旅游'", "加上'秋季'", "加上'照片'"]
-
-### 6. new_query
-最终的新query
-
-## 重要原则
-
-1. **推荐词是最宝贵的反馈** - 充分利用推荐词这个客观信号
-   - 即使essence=0的推荐词,也揭示了系统在这个方向有什么
-   - **优先考虑use_recommendation策略** - 选一个推荐词作为起点
-
-2. **中间query可以偏离原需求** - 每一步都有明确的探索目的
-   - 不要纠结"这个query不满足原需求"
-   - 关键是:这个query能不能帮你往正确方向引导系统
-
-3. **识别死胡同,及时换方向**
-   - 如果多次尝试推荐词主题不变 → 换方向
-   - 如果推荐词越来越偏 → 回退到之前的某个好的起点
-
-4. **保持推理简洁** - 抓住关键信息
-   - 明确说出探索目的
-   - 不要重复啰嗦
-""".strip()
-
-class ModificationStrategy(BaseModel):
-    """修改策略模型 - 模拟人的搜索调整过程"""
-    reasoning: str = Field(..., description="推理过程:1)当前推荐词分析:系统理解成什么了?2)历史尝试总结:哪些方向有效/无效?3)下一步策略:为什么这样调整?")
-
-    strategy_type: Literal[
-        "refine_current",      # 微调当前query(加词/减词/换词/换顺序)
-        "use_recommendation",  # 选择推荐词作为新起点,在它基础上修改
-        "change_approach",     # 换完全不同的表述角度
-        "relax_constraints"    # 放宽约束,去掉部分限定词
-    ] = Field(..., description="策略类型")
-
-    base_query: str = Field(..., description="基础query,可以是:1)历史中的query 2)历史推荐词中的某一个")
-    base_query_source: Literal["history_query", "history_recommendation"] = Field(..., description="base_query的来源")
-
-    modification_actions: list[str] = Field(..., description="具体修改动作的描述,如:['去掉\"如何获取\"', '保留核心词\"川西秋季\"', '把\"素材\"改为\"图片\"']")
-
-    new_query: str = Field(..., description="修改后的新query")
-
-strategy_generator = Agent[None](
-    name="策略生成专家",
-    instructions=strategy_instructions,
-    output_type=ModificationStrategy,
-)
-
-
-# ============================================================================
-# 核心函数
-# ============================================================================
-
-async def annotate_question(q_with_context: str) -> str:
-    """标注问题(三层)"""
-    print("\n正在标注问题...")
-    result = await Runner.run(question_annotator, q_with_context)
-    annotation = str(result.final_output)
-    print(f"问题标注完成:{annotation}")
-    return annotation
-
-
-async def get_suggestions_with_eval(query: str, annotation: str, context: RunContext) -> list[dict]:
-    """获取推荐词并评估"""
-    print(f"\n正在获取推荐词:{query}")
-
-    # 1. 调用小红书API
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-    query_suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-    print(f"获取到 {len(query_suggestions) if query_suggestions else 0} 个推荐词:{query_suggestions}")
-
-    if not query_suggestions:
-        # 记录到历史
-        context.operations_history.append({
-            "operation_type": "get_query_suggestions",
-            "timestamp": datetime.now().isoformat(),
-            "query": query,
-            "suggestions": [],
-            "evaluations": "未返回任何推荐词",
-        })
-        return []
-
-    # 2. 并发评估所有推荐词
-    async def evaluate_single_query(q_sug: str):
-        eval_input = f"""
-<原始问题标注(三层)>
-{annotation}
-</原始问题标注(三层)>
-
-<待评估的推荐query>
-{q_sug}
-</待评估的推荐query>
-
-请评估该推荐query的三个分数:
-1. essence_score: 本质/意图是否一致(0或1)
-2. hard_score: 硬性约束是否满足(0或1)
-3. soft_score: 软性修饰保留程度(0-1)
-4. reason: 详细的评估理由
-"""
-        evaluator_result = await Runner.run(evaluator, eval_input)
-        result: EvaluationFeedback = evaluator_result.final_output
-        return {
-            "query": q_sug,
-            "essence_score": result.essence_score,
-            "hard_score": result.hard_score,
-            "soft_score": result.soft_score,
-            "reason": result.reason,
-        }
-
-    evaluations = await asyncio.gather(*[evaluate_single_query(q_sug) for q_sug in query_suggestions])
-
-    # 3. 记录到历史
-    context.operations_history.append({
-        "operation_type": "get_query_suggestions",
-        "timestamp": datetime.now().isoformat(),
-        "query": query,
-        "suggestions": query_suggestions,
-        "evaluations": evaluations,
-    })
-
-    return evaluations
-
-
-async def generate_modification_strategy(
-    current_query: str,
-    evaluations: list[dict],
-    annotation: str,
-    context: RunContext
-) -> ModificationStrategy:
-    """生成修改策略"""
-    print("\n正在生成修改策略...")
-
-    # 整理历史尝试记录 - 完整保留推荐词和评估结果
-    history_records = []
-    round_num = 0
-
-    for op in context.operations_history:
-        if op["operation_type"] == "get_query_suggestions":
-            round_num += 1
-            record = {
-                "round": round_num,
-                "query": op["query"],
-                "suggestions": op["suggestions"],
-                "evaluations": op["evaluations"]
-            }
-            history_records.append(record)
-        elif op["operation_type"] == "modify_query":
-            # 修改操作也记录,但不增加轮数
-            history_records.append({
-                "operation": "modify_query",
-                "strategy_type": op.get("strategy_type", op.get("modification_type")),  # 兼容旧字段
-                "base_query": op.get("base_query"),
-                "base_query_source": op.get("base_query_source"),
-                "modification_actions": op.get("modification_actions", []),
-                "original_query": op["original_query"],
-                "new_query": op["new_query"],
-                "reasoning": op["reasoning"]
-            })
-
-    # 格式化历史记录为JSON
-    history_json = json.dumps(history_records, ensure_ascii=False, indent=2)
-
-    strategy_input = f"""
-<原始问题标注(三层)>
-{annotation}
-</原始问题标注(三层)>
-
-<历史尝试记录(完整)>
-{history_json}
-</历史尝试记录(完整)>
-
-<当前query>
-{current_query}
-</当前query>
-
-<当前轮推荐词评估结果>
-{json.dumps(evaluations, ensure_ascii=False, indent=2) if evaluations else "空列表"}
-</当前轮推荐词评估结果>
-
-请基于所有历史尝试和当前评估结果,生成下一步的query修改策略。
-
-重点分析:
-
-1. **当前推荐词的信号**:
-   - 系统理解成什么主题了?(旅游?教程?素材?工具?品种?)
-   - 推荐词揭示了什么信息?系统在哪个方向有内容?
-   - **不要只看essence_score**:essence=0的推荐词也可能是好的探索起点
-   - 哪个推荐词最有潜力作为下一步的桥梁?
-
-2. **历史趋势分析**:
-   - 推荐词的主题变化:从"品种介绍"→"旅游"→"摄影"?
-   - 是否在逐步接近目标?还是原地打转(主题不变)?
-   - 哪些query让系统理解方向改变了?
-
-3. **确定探索目的**:
-   - 下一步query的探索目的是什么?
-     * 验证系统对某个词的理解?
-     * 往某个方向引导系统?
-     * 利用推荐词作为桥梁?
-   - **记住**:中间query不需要满足原需求,关键是达成探索目的
-"""
-    result = await Runner.run(strategy_generator, strategy_input)
-    strategy: ModificationStrategy = result.final_output
-    return strategy
-
-
-def find_qualified_queries(evaluations: list[dict], min_soft_score: float = 0.7) -> list[dict]:
-    """查找所有合格的query,按soft_score降序排列"""
-    qualified = [
-        e for e in evaluations
-        if e['essence_score'] == 1
-        and e['hard_score'] == 1
-        and e['soft_score'] >= min_soft_score
-    ]
-    # 按soft_score降序排列
-    return sorted(qualified, key=lambda x: x['soft_score'], reverse=True)
-
-
-# ============================================================================
-# 主流程(代码控制)
-# ============================================================================
-
-async def optimize_query(context: RunContext, max_rounds: int = 20) -> dict:
-    """
-    主优化流程 - 由代码控制
-
-    Args:
-        context: 运行上下文
-        max_rounds: 最大迭代轮数,默认20
-
-    返回格式:
-    {
-        "success": True/False,
-        "result": {...} or None,
-        "message": "..."
-    }
-    """
-    # 1. 标注问题(仅一次)
-    annotation = await annotate_question(context.q_with_context)
-    context.question_annotation = annotation
-
-    # 2. 迭代优化
-    current_query = context.q
-
-    for round_num in range(1, max_rounds + 1):
-        print(f"\n{'='*60}")
-        print(f"第 {round_num} 轮:{'使用原始问题' if round_num == 1 else '使用修改后的query'}")
-        print(f"当前query: {current_query}")
-        print(f"{'='*60}")
-
-        # 获取推荐词并评估
-        evaluations = await get_suggestions_with_eval(current_query, annotation, context)
-
-        if evaluations:
-            # 检查是否找到合格query
-            qualified_queries = find_qualified_queries(evaluations, min_soft_score=0.7)
-            if qualified_queries:
-                return {
-                    "success": True,
-                    "results": qualified_queries,
-                    "message": f"第{round_num}轮找到{len(qualified_queries)}个合格query"
-                }
-
-        # 如果是最后一轮,不再生成策略
-        if round_num == max_rounds:
-            break
-
-        # 生成修改策略
-        print(f"\n--- 生成修改策略 ---")
-        strategy = await generate_modification_strategy(current_query, evaluations, annotation, context)
-
-        print(f"\n修改策略:")
-        print(f"  推理过程:{strategy.reasoning}")
-        print(f"  策略类型:{strategy.strategy_type}")
-        print(f"  基础query:{strategy.base_query} (来源: {strategy.base_query_source})")
-        print(f"  修改动作:{', '.join(strategy.modification_actions)}")
-        print(f"  新query:{strategy.new_query}")
-
-        # 记录修改
-        context.operations_history.append({
-            "operation_type": "modify_query",
-            "timestamp": datetime.now().isoformat(),
-            "reasoning": strategy.reasoning,
-            "strategy_type": strategy.strategy_type,
-            "base_query": strategy.base_query,
-            "base_query_source": strategy.base_query_source,
-            "modification_actions": strategy.modification_actions,
-            "original_query": current_query,
-            "new_query": strategy.new_query,
-        })
-
-        # 更新当前query
-        current_query = strategy.new_query
-
-    # 所有轮次后仍未找到,降低标准查找
-    print(f"\n{'='*60}")
-    print(f"{max_rounds}轮后未找到最优query,降低标准(soft_score >= 0.5)")
-    print(f"{'='*60}")
-
-    acceptable_queries = find_qualified_queries(evaluations, min_soft_score=0.5)
-    if acceptable_queries:
-        return {
-            "success": True,
-            "results": acceptable_queries,
-            "message": f"{max_rounds}轮后找到{len(acceptable_queries)}个可接受query(soft_score >= 0.5)"
-        }
-
-    # 完全失败:找出最接近的
-    essence_hard_ok = [
-        e for e in evaluations
-        if e['essence_score'] == 1 and e['hard_score'] == 1
-    ]
-    if essence_hard_ok:
-        # 返回所有满足essence和hard的,按soft_score降序
-        closest_queries = sorted(essence_hard_ok, key=lambda x: x['soft_score'], reverse=True)
-        return {
-            "success": False,
-            "results": closest_queries,
-            "message": f"未找到合格query,但有{len(closest_queries)}个接近的推荐词(essence=1, hard=1)"
-        }
-
-    return {
-        "success": False,
-        "results": [],
-        "message": "未找到任何满足本质和硬性约束的推荐词"
-    }
-
-
-# ============================================================================
-# 输出格式化
-# ============================================================================
-
-def format_output(optimization_result: dict, context: RunContext) -> str:
-    """格式化输出结果"""
-    results = optimization_result.get("results", [])
-
-    if optimization_result["success"] and results:
-        output = f"原始问题:{context.q}\n"
-        output += f"状态:{optimization_result['message']}\n\n"
-        output += "合格的推荐query(按soft_score降序):\n"
-        for i, result in enumerate(results, 1):
-            output += f"\n{i}. {result['query']}\n"
-            output += f"   - 本质匹配度:{result['essence_score']} (1=本质一致)\n"
-            output += f"   - 硬性约束匹配度:{result['hard_score']} (1=所有约束满足)\n"
-            output += f"   - 软性修饰完整度:{result['soft_score']:.2f} (0-1)\n"
-            output += f"   - 评估理由:{result['reason']}\n"
-        return output.strip()
-    else:
-        output = f"原始问题:{context.q}\n"
-        output += f"结果:未找到合格推荐query\n"
-        output += f"原因:{optimization_result['message']}\n"
-
-        if results:
-            output += "\n最接近的推荐词(按soft_score降序):\n"
-            for i, result in enumerate(results[:3], 1):  # 只显示前3个
-                output += f"\n{i}. {result['query']}\n"
-                output += f"   - essence_score: {result['essence_score']}\n"
-                output += f"   - hard_score: {result['hard_score']}\n"
-                output += f"   - soft_score: {result['soft_score']:.2f}\n"
-                output += f"   - reason: {result['reason']}\n"
-
-        output += "\n建议:尝试简化问题或调整需求描述"
-        return output.strip()
-
-
-# ============================================================================
-# 主函数
-# ============================================================================
-
-async def main(input_dir: str, max_rounds: int = 20):
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]
-
-    # 日志保存目录
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-
-    # 执行优化流程(代码控制)
-    optimization_result = await optimize_query(run_context, max_rounds=max_rounds)
-
-    # 格式化输出
-    final_output = format_output(optimization_result, run_context)
-    print(f"\n{'='*60}")
-    print("最终结果")
-    print(f"{'='*60}")
-    print(final_output)
-
-    # 保存结果
-    run_context.optimization_result = optimization_result
-    run_context.final_output = final_output
-
-    # 保存 RunContext 到 log_dir
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    parser.add_argument(
-        "--max-rounds",
-        type=int,
-        default=20,
-        help="最大迭代轮数,默认: 20"
-    )
-    args = parser.parse_args()
-
-    asyncio.run(main(args.input_dir, max_rounds=args.max_rounds))

+ 0 - 965
sug_v5_0_with_eval_v2_yx.py

@@ -1,965 +0,0 @@
-import asyncio
-import json
-import os
-import argparse
-from datetime import datetime
-
-from agents import Agent, Runner
-from lib.my_trace import set_trace
-from typing import Literal
-from pydantic import BaseModel, Field
-
-from lib.utils import read_file_as_string
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-    question_annotation: str | None = Field(default=None, description="问题的标注结果")
-    operations_history: list[dict] = Field(default_factory=list, description="记录所有操作的历史")
-    optimization_result: dict | None = Field(default=None, description="最终优化结果对象")
-    final_output: str | None = Field(default=None, description="最终输出结果(格式化文本)")
-
-
-# ============================================================================
-# Agent 1: 问题标注专家
-# ============================================================================
-question_annotation_instructions = """
-你是搜索需求分析专家。给定问题(含需求背景),在原文上标注三层:本质、硬性、软性。
-
-## 判断标准
-
-**[本质]** - 问题的核心意图
-- 如何获取、教程、推荐、作品、测评等
-
-**[硬]** - 客观事实性约束(可明确验证、非主观判断)
-- 能明确区分类别的:地域、时间、对象、工具、操作类型
-- 特征:改变后得到完全不同类别的结果
-
-**[软]** - 主观判断性修饰(因人而异、程度性的)
-- 需要主观评价的:质量、速度、美观、特色、程度
-- 特征:改变后仍是同类结果,只是满足程度不同
-
-## 输出格式
-
-词语[本质-描述]、词语[硬-描述]、词语[软-描述]
-
-## 注意
-- 只输出标注后的字符串
-- 结合需求背景判断意图
-""".strip()
-
-question_annotator = Agent[None](
-    name="问题标注专家",
-    instructions=question_annotation_instructions,
-)
-
-
-# ============================================================================
-# Agent 2: 评估专家
-# ============================================================================
-eval_instructions = """
-你是专业的语言专家和语义相关性评判专家。你的任务是判断平台sug词条与原始query问题的相关度满足度。
-
-## 评估目标
-
-用这个推荐query搜索,能否找到满足原始需求的内容?
-
-## 三层判定流程(一次性返回所有层级的评分)
-
-### 第一层:知识识别(knowledge_recognition = 0 或 1)
-
-**什么是知识?**
-在社交媒体创作场景下,知识是指:可应用的认知内容 + 实践指导 + 问题解决方案
-
-包含三个核心要素:
-- 陈述性知识(Know-What): 是什么、有哪些、包括什么
-- 程序性知识(Know-How): 如何做、怎么实现、步骤方法
-- 策略性知识(Know-Why): 为什么、原理机制、优化策略
-
-**判定方法(三步判定法):**
-
-Step 1: 意图识别
-- 原始需求是想【知道/学会/获得】某样东西吗?→ yes 进入step2
-
-Step 2: 动词解析
-- 提取核心动词:
-  - 认知类(了解、学习、理解)
-  - 操作类(制作、拍摄、剪辑、运营)
-  - 获取类(找、下载、获取、收集)
-  - 决策类(选择、对比、评估)
-- 有明确动词 → 是知识需求
-- 无明确动词但有隐含目的 → 提取隐含动词
-- 完全无动作意图 → 非知识需求
-
-Step 3: 目标验证
-- 这个query解决后,用户会获得新的认知或能力吗? → YES则是知识
-- 这个query的答案可以被学习和应用吗? → YES则是知识
-- 这个query在寻求某个问题的解决方案吗? → YES则是知识
-
-**输出:**
-- knowledge_recognition: 1=是知识需求,0=非知识需求
-- knowledge_recognition_reason: 判定依据(包含意图识别、动词提取、目标验证的关键发现)
-
-**重要:即使knowledge_recognition=0,也要继续计算后两层得分(便于分析)**
-
----
-
-### 第二层:知识动机判定(motivation_score = 0-1分值)
-
-**目标:** 完全理解原始query的综合意图,识别主要需求和次要需求,并进行加权评估。
-
-**评估维度:**
-
-#### 维度1: 核心意图动词识别(权重50%)
-- 显性动词直接提取:如"如何获取素材" → 核心动作="获取"
-- 隐性动词语义推理:如"川西秋天风光摄影" → 隐含动作="拍摄"或"欣赏"
-- 动作层级区分:主动作 vs 子动作
-
-**评分规则:**
-- 核心动作完全一致 → 1.0
-- 核心动作语义相近(近义词) → 0.8-0.9
-- 核心动作有包含关系(主次关系) → 0.5-0.7
-- 核心动作完全不同 → 0-0.4
-
-#### 维度2: 目标对象识别(权重30%)
-- 主要对象(What):如"获取川西秋季风光摄影素材" → 主要对象="风光摄影素材"
-- 对象的限定词:地域限定("川西")、时间限定("秋季")、质量限定("高质量")
-
-**评分规则:**
-- 主要对象+核心限定词完全匹配 → 1.0
-- 主要对象匹配,限定词部分匹配 → 0.7-0.9
-- 主要对象匹配,限定词缺失/不符 → 0.4-0.6
-- 主要对象不匹配 → 0-0.3
-
-#### 维度3: 终极目的识别(权重20%)
-**评分规则:**
-- 目的完全一致 → 1.0
-- 目的相关但路径不同 → 0.6-0.7
-- 目的不相关 → 0-0.5
-
-**综合计算公式:**
-```
-motivation_score = 核心意图动词×0.5 + 目标对象×0.3 + 终极目的×0.2
-```
-
-**输出:**
-- motivation_score: 0-1分值(>=0.9才算通过)
-- motivation_breakdown: {"核心意图动词": 0.x, "目标对象": 0.x, "终极目的": 0.x}
-
-**阈值判定:**
-- >=0.9:意图高度匹配
-- <0.9:意图不匹配,建议重新生成sug词或调整query词
-
-**注意:评估标准需要严格,对所有用例保持一致的标准**
-
----
-
-### 第三层:相关性判定(relevance_score = 0-1分值)
-
-**目标:** 基于第二层的综合意图,评估sug词条对原始query的满足程度。
-
-**评分标准体系:**
-
-#### 高度满足(0.9-1.0)
-- 核心动作:完全一致或为标准近义词
-- 目标对象:主体+关键限定词全部匹配
-- 使用场景:完全相同或高度兼容
-- 终极目的:完全一致
-- 判定方法:逐一核对,所有维度≥0.9;替换测试(把sug词替换原query,意思不变)
-
-#### 中高满足(0.7-0.89)
-- 核心动作:一致或相近,但可能更泛化/具体化
-- 目标对象:主体匹配,但1-2个限定词缺失/泛化
-- 使用场景:基本兼容,可能略有扩展或收窄
-- 终极目的:一致但实现路径略有差异
-- 判定方法:"有效信息保留率" ≥70%
-
-#### 中低满足(0.4-0.69)
-- 核心动作:存在明显差异,但主题相关
-- 目标对象:部分匹配,关键限定词缺失或错位
-- 使用场景:有关联但场景不同
-- 终极目的:相关但实现路径完全不同
-- 判定方法:只有主题词重叠,用户需要显著改变搜索策略
-
-#### 低度/不满足(0-0.39)
-- 核心动作:完全不同或对立
-- 目标对象:主体不同或无关联
-- 使用场景:场景冲突
-- 终极目的:完全不相关
-- 判定方法:除通用词外无有效重叠,sug词满足了完全不同的需求
-
-**维度计算公式:**
-```
-relevance_score = 核心动作×0.4 + 目标对象×0.3 + 使用场景×0.15 + 终极目的×0.15
-```
-
-**特殊情况处理:**
-
-1. 泛化与具体化
-   - 泛化(sug词更广):如果原query所有要素都在覆盖范围内 → 0.75-0.85
-   - 具体化(sug词更窄):如果sug词是原query的典型子场景 → 0.7-0.8
-
-2. 同义转换宽容度
-   - 允许:获取≈下载≈寻找≈收集;技巧≈方法≈教程≈攻略
-   - 不允许:获取素材≠制作素材;学习技巧≠查看案例
-
-3. 多意图处理
-   - 识别主次意图(通过语序、连接词判断)
-   - sug词至少满足主意图 → 中高满足
-   - sug词同时满足主次意图 → 高度满足
-   - sug词只满足次意图 → 降至中低满足
-
-**输出:**
-- relevance_score: 0-1分值(>=0.9为高度满足)
-- relevance_breakdown: {"核心动作": 0.x, "目标对象": 0.x, "使用场景": 0.x, "终极目的": 0.x}
-
----
-
-## 最终输出要求
-
-一次性返回所有三层的评估结果:
-1. knowledge_recognition (0或1) + knowledge_recognition_reason
-2. motivation_score (0-1) + motivation_breakdown
-3. relevance_score (0-1) + relevance_breakdown
-4. overall_reason: 综合评估理由(简要总结三层判定结果)
-
-**重要原则:**
-- 即使第一层knowledge_recognition=0,也要完整计算第二层和第三层
-- 即使第二层motivation_score<0.9,也要完整计算第三层
-- 所有维度的breakdown必须提供具体数值
-- 评估标准严格一致,不因用例不同而放松标准
-""".strip()
-
-class MotivationBreakdown(BaseModel):
-    """动机得分明细"""
-    核心意图动词: float = Field(..., description="核心意图动词得分,0-1")
-    目标对象: float = Field(..., description="目标对象得分,0-1")
-    终极目的: float = Field(..., description="终极目的得分,0-1")
-
-
-class RelevanceBreakdown(BaseModel):
-    """相关性得分明细"""
-    核心动作: float = Field(..., description="核心动作得分,0-1")
-    目标对象: float = Field(..., description="目标对象得分,0-1")
-    使用场景: float = Field(..., description="使用场景得分,0-1")
-    终极目的: float = Field(..., description="终极目的得分,0-1")
-
-
-class EvaluationFeedback(BaseModel):
-    """评估反馈模型 - 三层知识评估"""
-    # 第一层:知识识别
-    knowledge_recognition: Literal[0, 1] = Field(
-        ...,
-        description="是否为知识需求,1=是,0=否"
-    )
-    knowledge_recognition_reason: str = Field(
-        ...,
-        description="知识识别判定依据(意图识别、动词提取、目标验证)"
-    )
-
-    # 第二层:知识动机匹配
-    motivation_score: float = Field(
-        ...,
-        description="知识动机匹配度,0-1分值,>=0.9才通过"
-    )
-    motivation_breakdown: MotivationBreakdown = Field(
-        ...,
-        description="动机得分明细"
-    )
-
-    # 第三层:相关性评分
-    relevance_score: float = Field(
-        ...,
-        description="相关性得分,0-1分值,>=0.9为高度满足"
-    )
-    relevance_breakdown: RelevanceBreakdown = Field(
-        ...,
-        description="相关性得分明细"
-    )
-
-    overall_reason: str = Field(
-        ...,
-        description="综合评估理由"
-    )
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_instructions,
-    output_type=EvaluationFeedback,
-)
-
-
-# ============================================================================
-# Agent 3: 修改策略生成专家
-# ============================================================================
-strategy_instructions = """
-你是query修改策略专家。**模拟人在搜索引擎中的真实搜索行为**,基于反馈动态调整query。
-
-## 核心思路:搜索是探索过程,不是直达过程
-
-**关键认知:**
-1. **中间query不需要满足原始需求** - 它是探索工具,可以偏离原需求
-2. **推荐词是最宝贵的反馈信号** - 告诉你系统理解成什么了,有什么内容
-3. **每一步query都有明确的探索目的** - 不是盲目改词,而是试探和引导
-4. **最终目标:找到满足需求的推荐词** - 不是让query本身满足需求
-
-## 人的真实搜索过程
-
-**搜索的本质**:通过多步探索,利用推荐词作为桥梁,逐步引导系统
-
-**典型模式**:
-
-第1步:直接尝试
-- 目的:看系统能否直接理解
-- 结果:空列表或essence=0
-- essence=0的推荐词:告诉你系统理解成什么了
-
-第2步:降低要求,简化query
-- 目的:让系统有响应,看它在基础层面有什么
-- 推荐词虽然essence=0,但揭示了系统在某个主题有内容
-- **关键**:选一个最有潜力的推荐词
-
-第3步:基于推荐词,往目标方向引导
-- 目的:利用推荐词作为桥梁,加上目标方向的词
-- 推荐词还是essence=0,但主题在变化(接近目标)
-- **渐进式**:不求一步到位,每步都有进展
-
-第4步:继续引导或换角度
-- 如果推荐词主题不变 → 换角度
-- 如果推荐词主题在接近 → 继续引导
-
-最终:找到essence=1的推荐词
-
-**关键原则**:
-1. essence_score是评估推荐词的,不是评估中间query的
-2. essence=0的推荐词也有价值,它揭示了系统的理解方向
-3. 每一步都有明确的探索目的,看目的是否达成
-4. 通过推荐词的主题变化,判断是否在接近目标
-
-## 输入信息
-- 原始问题标注(三层):本质、硬性约束、软性修饰
-- 历史尝试记录:所有轮次的query、推荐词、评估结果
-- 当前query和推荐词评估
-
-## 分析步骤
-
-### 第一步:理解当前推荐词的信号
-**核心问题:推荐词告诉我什么信息?**
-
-**重要提醒:essence_score是评估推荐词是否满足原始需求的最终目标**
-- essence_score=1: 推荐词满足原需求的本质
-- essence_score=0: 推荐词不满足原需求的本质
-- **但中间query的目的可能不是满足原需求**,所以essence_score只是参考
-
-1. **系统理解层面**(看推荐词的主题):
-   - 空列表 → 系统完全不理解当前query
-   - 有推荐词 → 系统理解成了什么主题?
-     - 旅游?教程?素材?工具?品种介绍?
-     - 这些主题是否有助于往目标方向引导?
-
-2. **内容可用性层面**(看推荐词的价值):
-   - **即使推荐词essence=0,也可能是很好的探索起点**
-   - 例如:推荐词"川西旅游攻略"虽然essence=0,但揭示了系统认识"川西"
-   - 哪些推荐词最有潜力作为下一步的桥梁?
-
-3. **探索目的验证**:
-   - 当前query的探索目的是什么?达到了吗?
-   - 例如:目的是"看系统对川西有什么" → 达到了(有推荐词)
-   - 下一步要验证/探索什么?
-
-### 第二步:回顾历史,识别规律
-- 哪些query让系统理解方向变化了?(从"旅游"变成"摄影")
-- 哪些方向是死路?(多次essence=0且推荐词主题不变)
-- **是否有渐进的改善?**(推荐词越来越接近目标)
-
-### 第三步:选择策略类型(带着明确的探索目的)
-
-**refine_current(微调当前query)**
-- 适用:推荐词方向对了,需要微调让它更精确
-- 探索目的:在正确方向上精细化
-- 动作:加词/减词/换词/调整顺序
-
-**use_recommendation(选推荐词作为新起点)** ⭐ 最重要策略
-- 适用:推荐词虽然essence=0,但**揭示了系统在这个方向有内容**
-- 探索目的:利用推荐词这个客观信号,引导系统往目标方向
-- **核心思维**:推荐词是系统给你的提示,告诉你"我有这个"
-- 动作:
-  - 选一个最有潜力的推荐词作为base_query
-  - 在它基础上加目标方向的词
-  - **这个新query可能不满足原需求,但目的是探索和引导**
-
-**change_approach(换完全不同的角度)**
-- 适用:当前方向是死路(多次尝试推荐词主题不变)
-- 探索目的:跳出当前框架,从另一个角度切入
-- 动作:换一种完全不同的表述方式
-
-**relax_constraints(放宽约束)**
-- 适用:query太复杂,系统不理解(返回空列表)
-- 探索目的:先让系统有响应,看它在最基础层面有什么
-- 动作:去掉限定词,保留核心概念
-
-## 输出要求
-
-### 1. reasoning(推理过程)
-必须包含三部分,**重点写探索目的**:
-
-- **当前推荐词信号分析**:
-  - 系统理解成什么主题了?(旅游?教程?素材?工具?品种?)
-  - 推荐词揭示了什么信息?(系统在哪个方向有内容)
-  - **不要只看essence_score**:
-    - essence_score=0不代表推荐词没用
-    - 关键看推荐词的主题是否有助于引导
-  - 哪个推荐词最有潜力作为下一步的桥梁?
-
-- **历史尝试与趋势**:
-  - 系统理解的主题变化:从"品种介绍"→"旅游"→"摄影"
-  - 是否在逐步接近目标?还是原地打转?
-
-- **下一步策略与探索目的**:
-  - **这一步query的探索目的是什么?**
-    - 验证系统对某个词的理解?
-    - 往某个方向引导?
-    - 利用推荐词作为桥梁?
-  - 为什么选这个base_query?
-  - 为什么这样修改?
-  - **重要**:不要纠结"这个query不满足原需求",关键是它能否达成探索目的
-
-### 2. strategy_type
-从4种策略中选择:refine_current, use_recommendation, change_approach, relax_constraints
-
-### 3. base_query
-**关键**:可以选择历史中的query,也可以选择历史推荐词
-- 如果选历史query:base_query_source = "history_query"
-- 如果选历史推荐词:base_query_source = "history_recommendation"
-
-### 4. base_query_source
-说明base_query的来源
-
-### 5. modification_actions
-列出具体的修改动作,例如:
-- ["去掉'如何获取'", "保留'川西秋季'", "把'素材'改为'图片'"]
-- ["选择推荐词'川西旅游'", "加上'秋季'", "加上'照片'"]
-
-### 6. new_query
-最终的新query
-
-## 重要原则
-
-1. **推荐词是最宝贵的反馈** - 充分利用推荐词这个客观信号
-   - 即使essence=0的推荐词,也揭示了系统在这个方向有什么
-   - **优先考虑use_recommendation策略** - 选一个推荐词作为起点
-
-2. **中间query可以偏离原需求** - 每一步都有明确的探索目的
-   - 不要纠结"这个query不满足原需求"
-   - 关键是:这个query能不能帮你往正确方向引导系统
-
-3. **识别死胡同,及时换方向**
-   - 如果多次尝试推荐词主题不变 → 换方向
-   - 如果推荐词越来越偏 → 回退到之前的某个好的起点
-
-4. **保持推理简洁** - 抓住关键信息
-   - 明确说出探索目的
-   - 不要重复啰嗦
-""".strip()
-
-class ModificationStrategy(BaseModel):
-    """修改策略模型 - 模拟人的搜索调整过程"""
-    reasoning: str = Field(..., description="推理过程:1)当前推荐词分析:系统理解成什么了?2)历史尝试总结:哪些方向有效/无效?3)下一步策略:为什么这样调整?")
-
-    strategy_type: Literal[
-        "refine_current",      # 微调当前query(加词/减词/换词/换顺序)
-        "use_recommendation",  # 选择推荐词作为新起点,在它基础上修改
-        "change_approach",     # 换完全不同的表述角度
-        "relax_constraints"    # 放宽约束,去掉部分限定词
-    ] = Field(..., description="策略类型")
-
-    base_query: str = Field(..., description="基础query,可以是:1)历史中的query 2)历史推荐词中的某一个")
-    base_query_source: Literal["history_query", "history_recommendation"] = Field(..., description="base_query的来源")
-
-    modification_actions: list[str] = Field(..., description="具体修改动作的描述,如:['去掉\"如何获取\"', '保留核心词\"川西秋季\"', '把\"素材\"改为\"图片\"']")
-
-    new_query: str = Field(..., description="修改后的新query")
-
-strategy_generator = Agent[None](
-    name="策略生成专家",
-    instructions=strategy_instructions,
-    output_type=ModificationStrategy,
-)
-
-
-# ============================================================================
-# 核心函数
-# ============================================================================
-
-async def annotate_question(q_with_context: str) -> str:
-    """标注问题(三层)"""
-    print("\n正在标注问题...")
-    result = await Runner.run(question_annotator, q_with_context)
-    annotation = str(result.final_output)
-    print(f"问题标注完成:{annotation}")
-    return annotation
-
-
-async def get_suggestions_with_eval(query: str, annotation: str, context: RunContext) -> list[dict]:
-    """获取推荐词并评估"""
-    print(f"\n正在获取推荐词:{query}")
-
-    # 1. 调用小红书API
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-    query_suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-    print(f"获取到 {len(query_suggestions) if query_suggestions else 0} 个推荐词:{query_suggestions}")
-
-    if not query_suggestions:
-        # 记录到历史
-        context.operations_history.append({
-            "operation_type": "get_query_suggestions",
-            "timestamp": datetime.now().isoformat(),
-            "query": query,
-            "suggestions": [],
-            "evaluations": "未返回任何推荐词",
-        })
-        return []
-
-    # 2. 并发评估所有推荐词
-    async def evaluate_single_query(q_sug: str):
-        eval_input = f"""
-<需求上下文>
-{context.q_context}
-</需求上下文>
-
-<原始query问题>
-{context.q}
-</原始query问题>
-
-<平台sug词条>
-{q_sug}
-</平台sug词条>
-
-请对该sug词条进行三层评估(一次性返回所有层级的评分):
-
-第一层:判断sug词条是否为知识需求(knowledge_recognition: 0或1)
-第二层:评估知识动机匹配度(motivation_score: 0-1,需>=0.9)
-第三层:评估相关性得分(relevance_score: 0-1,>=0.9为高度满足)
-
-重要:即使第一层=0或第二层<0.9,也要完整计算所有层级的得分。
-"""
-        evaluator_result = await Runner.run(evaluator, eval_input)
-        result: EvaluationFeedback = evaluator_result.final_output
-        return {
-            "query": q_sug,
-            "knowledge_recognition": result.knowledge_recognition,
-            "knowledge_recognition_reason": result.knowledge_recognition_reason,
-            "motivation_score": result.motivation_score,
-            "motivation_breakdown": result.motivation_breakdown.model_dump(),
-            "relevance_score": result.relevance_score,
-            "relevance_breakdown": result.relevance_breakdown.model_dump(),
-            "overall_reason": result.overall_reason,
-        }
-
-    evaluations = await asyncio.gather(*[evaluate_single_query(q_sug) for q_sug in query_suggestions])
-
-    # 3. 记录到历史
-    context.operations_history.append({
-        "operation_type": "get_query_suggestions",
-        "timestamp": datetime.now().isoformat(),
-        "query": query,
-        "suggestions": query_suggestions,
-        "evaluations": evaluations,
-    })
-
-    return evaluations
-
-
-async def generate_modification_strategy(
-    current_query: str,
-    evaluations: list[dict],
-    annotation: str,
-    context: RunContext
-) -> ModificationStrategy:
-    """生成修改策略"""
-    print("\n正在生成修改策略...")
-
-    # 整理历史尝试记录 - 完整保留推荐词和评估结果
-    history_records = []
-    round_num = 0
-
-    for op in context.operations_history:
-        if op["operation_type"] == "get_query_suggestions":
-            round_num += 1
-            record = {
-                "round": round_num,
-                "query": op["query"],
-                "suggestions": op["suggestions"],
-                "evaluations": op["evaluations"]
-            }
-            history_records.append(record)
-        elif op["operation_type"] == "modify_query":
-            # 修改操作也记录,但不增加轮数
-            history_records.append({
-                "operation": "modify_query",
-                "strategy_type": op.get("strategy_type", op.get("modification_type")),  # 兼容旧字段
-                "base_query": op.get("base_query"),
-                "base_query_source": op.get("base_query_source"),
-                "modification_actions": op.get("modification_actions", []),
-                "original_query": op["original_query"],
-                "new_query": op["new_query"],
-                "reasoning": op["reasoning"]
-            })
-
-    # 格式化历史记录为JSON
-    history_json = json.dumps(history_records, ensure_ascii=False, indent=2)
-
-    strategy_input = f"""
-<原始问题标注(三层)>
-{annotation}
-</原始问题标注(三层)>
-
-<历史尝试记录(完整)>
-{history_json}
-</历史尝试记录(完整)>
-
-<当前query>
-{current_query}
-</当前query>
-
-<当前轮推荐词评估结果>
-{json.dumps(evaluations, ensure_ascii=False, indent=2) if evaluations else "空列表"}
-</当前轮推荐词评估结果>
-
-请基于所有历史尝试和当前评估结果,生成下一步的query修改策略。
-
-重点分析:
-
-1. **当前推荐词的信号**:
-   - 系统理解成什么主题了?(旅游?教程?素材?工具?品种?)
-   - 推荐词揭示了什么信息?系统在哪个方向有内容?
-   - **不要只看essence_score**:essence=0的推荐词也可能是好的探索起点
-   - 哪个推荐词最有潜力作为下一步的桥梁?
-
-2. **历史趋势分析**:
-   - 推荐词的主题变化:从"品种介绍"→"旅游"→"摄影"?
-   - 是否在逐步接近目标?还是原地打转(主题不变)?
-   - 哪些query让系统理解方向改变了?
-
-3. **确定探索目的**:
-   - 下一步query的探索目的是什么?
-     * 验证系统对某个词的理解?
-     * 往某个方向引导系统?
-     * 利用推荐词作为桥梁?
-   - **记住**:中间query不需要满足原需求,关键是达成探索目的
-"""
-    result = await Runner.run(strategy_generator, strategy_input)
-    strategy: ModificationStrategy = result.final_output
-    return strategy
-
-
-def find_qualified_queries(evaluations: list[dict]) -> dict:
-    """分级查找合格query
-
-    Returns:
-        {
-            "highly_qualified": [...],      # 高度满足:knowledge=1, motivation>=0.9, relevance>=0.9
-            "moderately_qualified": [...],  # 中高满足:knowledge=1, motivation>=0.9, relevance>=0.7
-            "lower_qualified": [...],       # 中低满足:knowledge=1, motivation>=0.9, relevance>=0.4
-        }
-    """
-    # 高度满足:knowledge=1, motivation>=0.9, relevance>=0.9
-    highly_qualified = [
-        e for e in evaluations
-        if e['knowledge_recognition'] == 1
-        and e['motivation_score'] >= 0.9
-        and e['relevance_score'] >= 0.9
-    ]
-
-    # 中高满足:knowledge=1, motivation>=0.9, 0.7<=relevance<0.9
-    moderately_qualified = [
-        e for e in evaluations
-        if e['knowledge_recognition'] == 1
-        and e['motivation_score'] >= 0.9
-        and 0.7 <= e['relevance_score'] < 0.9
-    ]
-
-    # 中低满足:knowledge=1, motivation>=0.9, 0.4<=relevance<0.7
-    lower_qualified = [
-        e for e in evaluations
-        if e['knowledge_recognition'] == 1
-        and e['motivation_score'] >= 0.9
-        and 0.4 <= e['relevance_score'] < 0.7
-    ]
-
-    return {
-        "highly_qualified": sorted(highly_qualified, key=lambda x: x['relevance_score'], reverse=True),
-        "moderately_qualified": sorted(moderately_qualified, key=lambda x: x['relevance_score'], reverse=True),
-        "lower_qualified": sorted(lower_qualified, key=lambda x: x['relevance_score'], reverse=True),
-    }
-
-
-# ============================================================================
-# 主流程(代码控制)
-# ============================================================================
-
-async def optimize_query(context: RunContext, max_rounds: int = 20) -> dict:
-    """
-    主优化流程 - 由代码控制
-
-    Args:
-        context: 运行上下文
-        max_rounds: 最大迭代轮数,默认20
-
-    返回格式:
-    {
-        "success": True/False,
-        "result": {...} or None,
-        "message": "..."
-    }
-    """
-    # 1. 标注问题(仅一次)
-    annotation = await annotate_question(context.q_with_context)
-    context.question_annotation = annotation
-
-    # 2. 迭代优化
-    current_query = context.q
-
-    for round_num in range(1, max_rounds + 1):
-        print(f"\n{'='*60}")
-        print(f"第 {round_num} 轮:{'使用原始问题' if round_num == 1 else '使用修改后的query'}")
-        print(f"当前query: {current_query}")
-        print(f"{'='*60}")
-
-        # 获取推荐词并评估
-        evaluations = await get_suggestions_with_eval(current_query, annotation, context)
-
-        if evaluations:
-            # 检查是否找到合格query(分级筛选)
-            qualified = find_qualified_queries(evaluations)
-
-            # 优先返回高度满足的query
-            if qualified["highly_qualified"]:
-                return {
-                    "success": True,
-                    "level": "highly_qualified",
-                    "results": qualified["highly_qualified"],
-                    "message": f"第{round_num}轮找到{len(qualified['highly_qualified'])}个高度满足的query(知识=1, 动机≥0.9, 相关性≥0.9)"
-                }
-
-            # 其次返回中高满足的query
-            if qualified["moderately_qualified"]:
-                return {
-                    "success": True,
-                    "level": "moderately_qualified",
-                    "results": qualified["moderately_qualified"],
-                    "message": f"第{round_num}轮找到{len(qualified['moderately_qualified'])}个中高满足的query(知识=1, 动机≥0.9, 相关性≥0.7)"
-                }
-
-        # 如果是最后一轮,不再生成策略
-        if round_num == max_rounds:
-            break
-
-        # 生成修改策略
-        print(f"\n--- 生成修改策略 ---")
-        strategy = await generate_modification_strategy(current_query, evaluations, annotation, context)
-
-        print(f"\n修改策略:")
-        print(f"  推理过程:{strategy.reasoning}")
-        print(f"  策略类型:{strategy.strategy_type}")
-        print(f"  基础query:{strategy.base_query} (来源: {strategy.base_query_source})")
-        print(f"  修改动作:{', '.join(strategy.modification_actions)}")
-        print(f"  新query:{strategy.new_query}")
-
-        # 记录修改
-        context.operations_history.append({
-            "operation_type": "modify_query",
-            "timestamp": datetime.now().isoformat(),
-            "reasoning": strategy.reasoning,
-            "strategy_type": strategy.strategy_type,
-            "base_query": strategy.base_query,
-            "base_query_source": strategy.base_query_source,
-            "modification_actions": strategy.modification_actions,
-            "original_query": current_query,
-            "new_query": strategy.new_query,
-        })
-
-        # 更新当前query
-        current_query = strategy.new_query
-
-    # 所有轮次后仍未找到高度/中高满足的,降低标准查找
-    print(f"\n{'='*60}")
-    print(f"{max_rounds}轮后未找到高度/中高满足的query,降低标准(相关性 >= 0.4)")
-    print(f"{'='*60}")
-
-    qualified = find_qualified_queries(evaluations)
-    if qualified["lower_qualified"]:
-        return {
-            "success": True,
-            "level": "lower_qualified",
-            "results": qualified["lower_qualified"],
-            "message": f"{max_rounds}轮后找到{len(qualified['lower_qualified'])}个中低满足的query(知识=1, 动机≥0.9, 相关性≥0.4)"
-        }
-
-    # 完全失败:找出最接近的(只满足知识识别,但动机不够)
-    knowledge_ok = [
-        e for e in evaluations
-        if e['knowledge_recognition'] == 1
-    ]
-    if knowledge_ok:
-        # 返回所有满足knowledge的,按motivation_score降序
-        closest_queries = sorted(knowledge_ok, key=lambda x: x['motivation_score'], reverse=True)
-        return {
-            "success": False,
-            "level": "failed",
-            "results": closest_queries[:5],  # 只返回前5个
-            "message": f"未找到合格query,但有{len(closest_queries)}个是知识需求(knowledge=1,但motivation<0.9)"
-        }
-
-    return {
-        "success": False,
-        "level": "failed",
-        "results": [],
-        "message": "未找到任何知识类推荐词(所有推荐词的knowledge_recognition均为0)"
-    }
-
-
-# ============================================================================
-# 输出格式化
-# ============================================================================
-
-def format_output(optimization_result: dict, context: RunContext) -> str:
-    """格式化输出结果"""
-    results = optimization_result.get("results", [])
-    level = optimization_result.get("level", "")
-
-    # 满足程度映射
-    level_map = {
-        "highly_qualified": "高度满足 (90-100%)",
-        "moderately_qualified": "中高满足 (70-89%)",
-        "lower_qualified": "中低满足 (40-69%)",
-        "failed": "未通过"
-    }
-
-    if optimization_result["success"] and results:
-        output = f"原始问题:{context.q}\n"
-        output += f"满足程度:{level_map.get(level, '未知')}\n"
-        output += f"状态:{optimization_result['message']}\n\n"
-        output += "推荐query(按相关性降序):\n"
-        for i, result in enumerate(results, 1):
-            output += f"\n{i}. {result['query']}\n"
-            output += f"   - 知识识别:{'是' if result['knowledge_recognition'] == 1 else '否'} ({result['knowledge_recognition_reason'][:50]}...)\n"
-            output += f"   - 动机匹配度:{result['motivation_score']:.2f} (≥0.9通过)\n"
-            output += f"     * 核心意图动词: {result['motivation_breakdown'].get('核心意图动词', 0):.2f}\n"
-            output += f"     * 目标对象: {result['motivation_breakdown'].get('目标对象', 0):.2f}\n"
-            output += f"     * 终极目的: {result['motivation_breakdown'].get('终极目的', 0):.2f}\n"
-            output += f"   - 相关性得分:{result['relevance_score']:.2f} (≥0.9高度满足)\n"
-            output += f"     * 核心动作: {result['relevance_breakdown'].get('核心动作', 0):.2f}\n"
-            output += f"     * 目标对象: {result['relevance_breakdown'].get('目标对象', 0):.2f}\n"
-            output += f"     * 使用场景: {result['relevance_breakdown'].get('使用场景', 0):.2f}\n"
-            output += f"     * 终极目的: {result['relevance_breakdown'].get('终极目的', 0):.2f}\n"
-            output += f"   - 综合评估:{result['overall_reason'][:100]}...\n"
-        return output.strip()
-    else:
-        output = f"原始问题:{context.q}\n"
-        output += f"结果:未找到合格推荐query\n"
-        output += f"满足程度:{level_map.get(level, '未知')}\n"
-        output += f"原因:{optimization_result['message']}\n"
-
-        if results:
-            output += "\n最接近的推荐词:\n"
-            for i, result in enumerate(results[:3], 1):  # 只显示前3个
-                output += f"\n{i}. {result['query']}\n"
-                output += f"   - 知识识别:{'是' if result['knowledge_recognition'] == 1 else '否'}\n"
-                output += f"   - 动机匹配度:{result['motivation_score']:.2f}\n"
-                output += f"   - 相关性得分:{result['relevance_score']:.2f}\n"
-                output += f"   - 综合评估:{result['overall_reason'][:100]}...\n"
-
-        output += "\n建议:尝试简化问题或调整需求描述"
-        return output.strip()
-
-
-# ============================================================================
-# 主函数
-# ============================================================================
-
-async def main(input_dir: str, max_rounds: int = 20):
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]
-
-    # 日志保存目录
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-
-    # 执行优化流程(代码控制)
-    optimization_result = await optimize_query(run_context, max_rounds=max_rounds)
-
-    # 格式化输出
-    final_output = format_output(optimization_result, run_context)
-    print(f"\n{'='*60}")
-    print("最终结果")
-    print(f"{'='*60}")
-    print(final_output)
-
-    # 保存结果
-    run_context.optimization_result = optimization_result
-    run_context.final_output = final_output
-
-    # 保存 RunContext 到 log_dir
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    parser.add_argument(
-        "--max-rounds",
-        type=int,
-        default=20,
-        help="最大迭代轮数,默认: 20"
-    )
-    args = parser.parse_args()
-
-    asyncio.run(main(args.input_dir, max_rounds=args.max_rounds))

+ 0 - 1016
sug_v5_0_with_eval_v2_zou_yx.py

@@ -1,1016 +0,0 @@
-import asyncio
-import json
-import os
-import argparse
-from datetime import datetime
-
-from agents import Agent, Runner
-from lib.my_trace import set_trace
-from typing import Literal
-from pydantic import BaseModel, Field
-
-from lib.utils import read_file_as_string
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-    question_annotation: str | None = Field(default=None, description="问题的标注结果")
-    operations_history: list[dict] = Field(default_factory=list, description="记录所有操作的历史")
-    optimization_result: dict | None = Field(default=None, description="最终优化结果对象")
-    final_output: str | None = Field(default=None, description="最终输出结果(格式化文本)")
-
-
-# ============================================================================
-# Agent 1: 问题标注专家
-# ============================================================================
-question_annotation_instructions = """
-你是搜索需求分析专家。给定问题(含需求背景),在原文上标注三层:本质、硬性、软性。
-
-## 判断标准
-
-**[本质]** - 问题的核心意图
-- 如何获取、教程、推荐、作品、测评等
-
-**[硬]** - 客观事实性约束(可明确验证、非主观判断)
-- 能明确区分类别的:地域、时间、对象、工具、操作类型
-- 特征:改变后得到完全不同类别的结果
-
-**[软]** - 主观判断性修饰(因人而异、程度性的)
-- 需要主观评价的:质量、速度、美观、特色、程度
-- 特征:改变后仍是同类结果,只是满足程度不同
-
-## 输出格式
-
-词语[本质-描述]、词语[硬-描述]、词语[软-描述]
-
-## 注意
-- 只输出标注后的字符串
-- 结合需求背景判断意图
-""".strip()
-
-question_annotator = Agent[None](
-    name="问题标注专家",
-    instructions=question_annotation_instructions,
-)
-
-
-# ============================================================================
-# Agent 2: 评估专家
-# ============================================================================
-eval_instructions = """
-你是专业的语言专家和语义相关性评判专家。你的任务是判断平台sug词条与原始query问题的相关度满足度。
-
-## 评估目标
-
-用这个推荐query搜索,能否找到满足原始需求的内容?
-
-## 三层判定流程(一次性返回所有层级的评分)
-
-### 第一层:知识识别(knowledge_recognition = 0 或 1)
-
-**什么是知识?**
-在社交媒体创作场景下,知识是指:可应用的认知内容 + 实践指导 + 问题解决方案
-
-包含三个核心要素:
-- 陈述性知识(Know-What): 是什么、有哪些、包括什么
-- 程序性知识(Know-How): 如何做、怎么实现、步骤方法
-- 策略性知识(Know-Why): 为什么、原理机制、优化策略
-
-**判定方法(三步判定法):**
-
-Step 1: 意图识别
-- 原始需求是想【知道/学会/获得】某样东西吗?→ yes 进入step2
-
-Step 2: 动词解析
-- 提取核心动词:
-  - 认知类(了解、学习、理解)
-  - 操作类(制作、拍摄、剪辑、运营)
-  - 获取类(找、下载、获取、收集)
-  - 决策类(选择、对比、评估)
-- 有明确动词 → 是知识需求
-- 无明确动词但有隐含目的 → 提取隐含动词
-- 完全无动作意图 → 非知识需求
-
-Step 3: 目标验证
-- 这个query解决后,用户会获得新的认知或能力吗? → YES则是知识
-- 这个query的答案可以被学习和应用吗? → YES则是知识
-- 这个query在寻求某个问题的解决方案吗? → YES则是知识
-
-**输出:**
-- knowledge_recognition: 1=是知识需求,0=非知识需求
-- knowledge_recognition_reason: 判定依据(包含意图识别、动词提取、目标验证的关键发现)
-
-**重要:即使knowledge_recognition=0,也要继续计算后两层得分(便于分析)**
-
----
-
-### 第二层:知识动机判定(motivation_score = 0-1分值)
-
-**目标:** 完全理解原始query的综合意图,识别主要需求和次要需求,并进行加权评估。
-
-**评估维度:**
-
-#### 维度1: 核心意图动词识别(权重50%)
-- 显性动词直接提取:如"如何获取素材" → 核心动作="获取"
-- 隐性动词语义推理:如"川西秋天风光摄影" → 隐含动作="拍摄"或"欣赏"
-- 动作层级区分:主动作 vs 子动作
-
-**评分规则:**
-- 核心动作完全一致 → 1.0
-- 核心动作语义相近(近义词) → 0.8-0.9
-- 核心动作有包含关系(主次关系) → 0.5-0.7
-- 核心动作完全不同 → 0-0.4
-
-#### 维度2: 目标对象识别(权重30%)
-- 主要对象(What):如"获取川西秋季风光摄影素材" → 主要对象="风光摄影素材"
-- 对象的限定词:地域限定("川西")、时间限定("秋季")、质量限定("高质量")
-
-**评分规则:**
-- 主要对象+核心限定词完全匹配 → 1.0
-- 主要对象匹配,限定词部分匹配 → 0.7-0.9
-- 主要对象匹配,限定词缺失/不符 → 0.4-0.6
-- 主要对象不匹配 → 0-0.3
-
-#### 维度3: 终极目的识别(权重20%)
-**评分规则:**
-- 目的完全一致 → 1.0
-- 目的相关但路径不同 → 0.6-0.7
-- 目的不相关 → 0-0.5
-
-**综合计算公式:**
-```
-motivation_score = 核心意图动词×0.5 + 目标对象×0.3 + 终极目的×0.2
-```
-
-**输出:**
-- motivation_score: 0-1分值(>=0.9才算通过)
-- motivation_breakdown: {"核心意图动词": 0.x, "目标对象": 0.x, "终极目的": 0.x}
-
-**阈值判定:**
-- >=0.9:意图高度匹配
-- <0.9:意图不匹配,建议重新生成sug词或调整query词
-
-**注意:评估标准需要严格,对所有用例保持一致的标准**
-
----
-
-### 第三层:相关性判定(relevance_score = 0-1分值)
-
-**目标:** 基于第二层的综合意图,评估sug词条对原始query的满足程度。
-
-**评分标准体系:**
-
-#### 高度满足(0.9-1.0)
-- 核心动作:完全一致或为标准近义词
-- 目标对象:主体+关键限定词全部匹配
-- 使用场景:完全相同或高度兼容
-- 终极目的:完全一致
-- 判定方法:逐一核对,所有维度≥0.9;替换测试(把sug词替换原query,意思不变)
-
-#### 中高满足(0.7-0.89)
-- 核心动作:一致或相近,但可能更泛化/具体化
-- 目标对象:主体匹配,但1-2个限定词缺失/泛化
-- 使用场景:基本兼容,可能略有扩展或收窄
-- 终极目的:一致但实现路径略有差异
-- 判定方法:"有效信息保留率" ≥70%
-
-#### 中低满足(0.4-0.69)
-- 核心动作:存在明显差异,但主题相关
-- 目标对象:部分匹配,关键限定词缺失或错位
-- 使用场景:有关联但场景不同
-- 终极目的:相关但实现路径完全不同
-- 判定方法:只有主题词重叠,用户需要显著改变搜索策略
-
-#### 低度/不满足(0-0.39)
-- 核心动作:完全不同或对立
-- 目标对象:主体不同或无关联
-- 使用场景:场景冲突
-- 终极目的:完全不相关
-- 判定方法:除通用词外无有效重叠,sug词满足了完全不同的需求
-
-**维度计算公式:**
-```
-relevance_score = 核心动作×0.4 + 目标对象×0.3 + 使用场景×0.15 + 终极目的×0.15
-```
-
-**特殊情况处理:**
-
-1. 泛化与具体化
-   - 泛化(sug词更广):如果原query所有要素都在覆盖范围内 → 0.75-0.85
-   - 具体化(sug词更窄):如果sug词是原query的典型子场景 → 0.7-0.8
-
-2. 同义转换宽容度
-   - 允许:获取≈下载≈寻找≈收集;技巧≈方法≈教程≈攻略
-   - 不允许:获取素材≠制作素材;学习技巧≠查看案例
-
-3. 多意图处理
-   - 识别主次意图(通过语序、连接词判断)
-   - sug词至少满足主意图 → 中高满足
-   - sug词同时满足主次意图 → 高度满足
-   - sug词只满足次意图 → 降至中低满足
-
-**输出:**
-- relevance_score: 0-1分值(>=0.9为高度满足)
-- relevance_breakdown: {"核心动作": 0.x, "目标对象": 0.x, "使用场景": 0.x, "终极目的": 0.x}
-
----
-
-## 最终输出要求
-
-一次性返回所有三层的评估结果:
-1. knowledge_recognition (0或1) + knowledge_recognition_reason
-2. motivation_score (0-1) + motivation_breakdown
-3. relevance_score (0-1) + relevance_breakdown
-4. overall_reason: 综合评估理由(简要总结三层判定结果)
-
-**重要原则:**
-- 即使第一层knowledge_recognition=0,也要完整计算第二层和第三层
-- 即使第二层motivation_score<0.9,也要完整计算第三层
-- 所有维度的breakdown必须提供具体数值
-- 评估标准严格一致,不因用例不同而放松标准
-""".strip()
-
-class MotivationBreakdown(BaseModel):
-    """动机得分明细"""
-    核心意图动词: float = Field(..., description="核心意图动词得分,0-1")
-    目标对象: float = Field(..., description="目标对象得分,0-1")
-    终极目的: float = Field(..., description="终极目的得分,0-1")
-
-
-class RelevanceBreakdown(BaseModel):
-    """相关性得分明细"""
-    核心动作: float = Field(..., description="核心动作得分,0-1")
-    目标对象: float = Field(..., description="目标对象得分,0-1")
-    使用场景: float = Field(..., description="使用场景得分,0-1")
-    终极目的: float = Field(..., description="终极目的得分,0-1")
-
-
-class EvaluationFeedback(BaseModel):
-    """评估反馈模型 - 三层知识评估"""
-    # 第一层:知识识别
-    knowledge_recognition: Literal[0, 1] = Field(
-        ...,
-        description="是否为知识需求,1=是,0=否"
-    )
-    knowledge_recognition_reason: str = Field(
-        ...,
-        description="知识识别判定依据(意图识别、动词提取、目标验证)"
-    )
-
-    # 第二层:知识动机匹配
-    motivation_score: float = Field(
-        ...,
-        description="知识动机匹配度,0-1分值,>=0.9才通过"
-    )
-    motivation_breakdown: MotivationBreakdown = Field(
-        ...,
-        description="动机得分明细"
-    )
-
-    # 第三层:相关性评分
-    relevance_score: float = Field(
-        ...,
-        description="相关性得分,0-1分值,>=0.9为高度满足"
-    )
-    relevance_breakdown: RelevanceBreakdown = Field(
-        ...,
-        description="相关性得分明细"
-    )
-
-    overall_reason: str = Field(
-        ...,
-        description="综合评估理由"
-    )
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_instructions,
-    output_type=EvaluationFeedback,
-)
-
-
-# ============================================================================
-# Agent 3: 修改策略生成专家
-# ============================================================================
-strategy_instructions = """
-你是query修改策略专家。**模拟人在搜索引擎中的真实搜索行为**,基于反馈动态调整query。
-
-## 核心思路:搜索是探索过程,不是直达过程
-
-**关键认知:**
-1. **中间query不需要满足原始需求** - 它是探索工具,可以偏离原需求
-2. **推荐词是最宝贵的反馈信号** - 告诉你系统理解成什么了,有什么内容
-3. **每一步query都有明确的探索目的** - 不是盲目改词,而是试探和引导
-4. **最终目标:找到满足需求的推荐词** - 不是让query本身满足需求
-
-## 人的真实搜索过程
-
-**搜索的本质**:通过多步探索,利用推荐词作为桥梁,逐步引导系统
-
-**典型模式**:
-
-第1步:直接尝试
-- 目的:看系统能否直接理解
-- 结果:空列表或essence=0
-- essence=0的推荐词:告诉你系统理解成什么了
-
-第2步:降低要求,简化query
-- 目的:让系统有响应,看它在基础层面有什么
-- 推荐词虽然essence=0,但揭示了系统在某个主题有内容
-- **关键**:选一个最有潜力的推荐词
-
-第3步:基于推荐词,往目标方向引导
-- 目的:利用推荐词作为桥梁,加上目标方向的词
-- 推荐词还是essence=0,但主题在变化(接近目标)
-- **渐进式**:不求一步到位,每步都有进展
-
-第4步:继续引导或换角度
-- 如果推荐词主题不变 → 换角度
-- 如果推荐词主题在接近 → 继续引导
-
-最终:找到essence=1的推荐词
-
-**关键原则**:
-1. essence_score是评估推荐词的,不是评估中间query的
-2. essence=0的推荐词也有价值,它揭示了系统的理解方向
-3. 每一步都有明确的探索目的,看目的是否达成
-4. 通过推荐词的主题变化,判断是否在接近目标
-
-## 输入信息
-- 原始问题标注(三层):本质、硬性约束、软性修饰
-- 历史尝试记录:所有轮次的query、推荐词、评估结果
-- 当前query和推荐词评估
-
-## 分析步骤
-
-### 第一步:理解当前推荐词的信号
-**核心问题:推荐词告诉我什么信息?**
-
-**重要提醒:essence_score是评估推荐词是否满足原始需求的最终目标**
-- essence_score=1: 推荐词满足原需求的本质
-- essence_score=0: 推荐词不满足原需求的本质
-- **但中间query的目的可能不是满足原需求**,所以essence_score只是参考
-
-1. **系统理解层面**(看推荐词的主题):
-   - 空列表 → 系统完全不理解当前query
-   - 有推荐词 → 系统理解成了什么主题?
-     - 旅游?教程?素材?工具?品种介绍?
-     - 这些主题是否有助于往目标方向引导?
-
-2. **内容可用性层面**(看推荐词的价值):
-   - **即使推荐词essence=0,也可能是很好的探索起点**
-   - 例如:推荐词"川西旅游攻略"虽然essence=0,但揭示了系统认识"川西"
-   - 哪些推荐词最有潜力作为下一步的桥梁?
-
-3. **探索目的验证**:
-   - 当前query的探索目的是什么?达到了吗?
-   - 例如:目的是"看系统对川西有什么" → 达到了(有推荐词)
-   - 下一步要验证/探索什么?
-
-### 第二步:回顾历史,识别规律
-- 哪些query让系统理解方向变化了?(从"旅游"变成"摄影")
-- 哪些方向是死路?(多次essence=0且推荐词主题不变)
-- **是否有渐进的改善?**(推荐词越来越接近目标)
-
-### 第三步:选择策略类型和具体操作(带着明确的探索目的)
-
-**策略类型(strategy_type):**
-
-**refine_current(微调当前query)**
-- 适用:推荐词方向对了,需要微调让它更精确
-- 探索目的:在正确方向上精细化
-- 动作:加词/减词/换词/调整顺序
-
-**use_recommendation(选推荐词作为新起点)** ⭐ 最重要策略
-- 适用:推荐词虽然knowledge_recognition=0或relevance_score低,但**揭示了系统在这个方向有内容**
-- 探索目的:利用推荐词这个客观信号,引导系统往目标方向
-- **核心思维**:推荐词是系统给你的提示,告诉你"我有这个"
-- 动作:
-  - 选一个最有潜力的推荐词作为base_query
-  - 在它基础上加目标方向的词
-  - **这个新query可能不满足原需求,但目的是探索和引导**
-
-**change_approach(换完全不同的角度)**
-- 适用:当前方向是死路(多次尝试推荐词主题不变)
-- 探索目的:跳出当前框架,从另一个角度切入
-- 动作:换一种完全不同的表述方式
-
-**relax_constraints(放宽约束)**
-- 适用:query太复杂,系统不理解(返回空列表)
-- 探索目的:先让系统有响应,看它在最基础层面有什么
-- 动作:去掉限定词,保留核心概念
-
----
-
-**具体操作类型(operation_type):**
-
-**增加**
-- **判定标准**:
-  * 推荐词的语义比当前query更具体,且词汇数量 > 当前query的词汇数量
-  * 推荐词包含当前query的核心关键词,且添加了能将query具体化的新词
-  * 推荐词中存在当前query中不存在的关键新词(如:特定平台名称、特定功能词、具体场景词)
-- **使用时机**:当推荐词整体趋势是添加更具体的限定词或场景描述时
-
-**删减**
-- **判定标准**(满足以下任一条件):
-  * 推荐词的词汇数量明显少于当前query
-  * 推荐词返回空列表(说明query过于复杂)
-  * 推荐词词汇数量少于query,但核心搜索意图一致
-  * 识别出query中1-2个词为"非核心冗余词",删除后不影响核心意图
-- **使用时机**:当原query过于冗长复杂,推荐词都倾向于使用更简洁的表达时
-
-**调序**
-- **判定标准**:
-  * 推荐词与query拥有完全相同或极为相似的核心关键词集合,只是词汇排列顺序不同
-  * 推荐词的词序更自然或更符合用户搜索习惯
-  * 多个推荐词都倾向于使用与query不同但意思一致的词序
-- **使用时机**:当推荐词与原query关键词相同但顺序不同,且新顺序更符合用户习惯时
-
-**替换**
-- **判定标准**:
-  * 推荐词与query仅有1-2个核心词不同,其他词均相同
-  * 差异词是同义词、近义词或在该领域更流行/专业的替代词
-  * 推荐词中的新词在推荐列表中出现频率更高
-- **使用时机**:当推荐词显示某个词的同义词或专业术语更受欢迎时
-
-## 输出要求
-
-### 1. reasoning(推理过程)
-必须包含三部分,**重点写探索目的**:
-
-- **当前推荐词信号分析**:
-  - 系统理解成什么主题了?(旅游?教程?素材?工具?品种?)
-  - 推荐词揭示了什么信息?(系统在哪个方向有内容)
-  - **不要只看knowledge_recognition和relevance_score**:
-    - knowledge_recognition=0或relevance_score低不代表推荐词没用
-    - 关键看推荐词的主题是否有助于引导
-  - 哪个推荐词最有潜力作为下一步的桥梁?
-
-- **历史尝试与趋势**:
-  - 系统理解的主题变化:从"品种介绍"→"旅游"→"摄影"
-  - 是否在逐步接近目标?还是原地打转?
-
-- **下一步策略与探索目的**:
-  - **这一步query的探索目的是什么?**
-    - 验证系统对某个词的理解?
-    - 往某个方向引导?
-    - 利用推荐词作为桥梁?
-  - 为什么选这个base_query?
-  - 为什么选这个operation_type?
-  - 为什么这样修改?
-  - **重要**:不要纠结"这个query不满足原需求",关键是它能否达成探索目的
-
-### 2. strategy_type
-从4种策略中选择:refine_current, use_recommendation, change_approach, relax_constraints
-
-### 3. operation_type
-从4种操作中选择:增加、删减、调序、替换
-- **必须基于推荐词的特征选择**:看推荐词与当前query的差异模式
-- 参考上述"具体操作类型"的判定标准
-
-### 4. base_query
-**关键**:可以选择历史中的query,也可以选择历史推荐词
-- 如果选历史query:base_query_source = "history_query"
-- 如果选历史推荐词:base_query_source = "history_recommendation"
-
-### 5. base_query_source
-说明base_query的来源
-
-### 6. modification_actions
-列出具体的修改动作,建议格式:
-- 每个动作以操作类型开头(增加/删减/调序/替换)
-- 清晰描述具体修改了什么内容
-
-### 7. new_query
-最终的新query
-
-## 重要原则
-
-1. **推荐词是最宝贵的反馈** - 充分利用推荐词这个客观信号
-   - 即使essence=0的推荐词,也揭示了系统在这个方向有什么
-   - **优先考虑use_recommendation策略** - 选一个推荐词作为起点
-
-2. **中间query可以偏离原需求** - 每一步都有明确的探索目的
-   - 不要纠结"这个query不满足原需求"
-   - 关键是:这个query能不能帮你往正确方向引导系统
-
-3. **识别死胡同,及时换方向**
-   - 如果多次尝试推荐词主题不变 → 换方向
-   - 如果推荐词越来越偏 → 回退到之前的某个好的起点
-
-4. **保持推理简洁** - 抓住关键信息
-   - 明确说出探索目的
-   - 不要重复啰嗦
-""".strip()
-
-class ModificationStrategy(BaseModel):
-    """修改策略模型 - 模拟人的搜索调整过程"""
-    reasoning: str = Field(..., description="推理过程:1)当前推荐词分析:系统理解成什么了?2)历史尝试总结:哪些方向有效/无效?3)下一步策略:为什么这样调整?")
-
-    strategy_type: Literal[
-        "refine_current",      # 微调当前query(加词/减词/换词/换顺序)
-        "use_recommendation",  # 选择推荐词作为新起点,在它基础上修改
-        "change_approach",     # 换完全不同的表述角度
-        "relax_constraints"    # 放宽约束,去掉部分限定词
-    ] = Field(..., description="策略类型")
-
-    operation_type: Literal[
-        "增加",  # 增加query语句,添加具体化的关键词
-        "删减",  # 删减query词,去除冗余词汇
-        "调序",  # 调整query词的顺序,更符合用户习惯的词序
-        "替换",  # 替换query中的某个词,使用同义词或专业术语
-    ] = Field(..., description="具体操作类型:增加、删减、调序、替换")
-
-    base_query: str = Field(..., description="基础query,可以是:1)历史中的query 2)历史推荐词中的某一个")
-    base_query_source: Literal["history_query", "history_recommendation"] = Field(..., description="base_query的来源")
-
-    modification_actions: list[str] = Field(..., description="具体修改动作的描述,如:['去掉\"如何获取\"', '保留核心词\"川西秋季\"', '把\"素材\"改为\"图片\"']")
-
-    new_query: str = Field(..., description="修改后的新query")
-
-strategy_generator = Agent[None](
-    name="策略生成专家",
-    instructions=strategy_instructions,
-    output_type=ModificationStrategy,
-)
-
-
-# ============================================================================
-# 核心函数
-# ============================================================================
-
-async def annotate_question(q_with_context: str) -> str:
-    """标注问题(三层)"""
-    print("\n正在标注问题...")
-    result = await Runner.run(question_annotator, q_with_context)
-    annotation = str(result.final_output)
-    print(f"问题标注完成:{annotation}")
-    return annotation
-
-
-async def get_suggestions_with_eval(query: str, annotation: str, context: RunContext) -> list[dict]:
-    """获取推荐词并评估"""
-    print(f"\n正在获取推荐词:{query}")
-
-    # 1. 调用小红书API
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-    query_suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-    print(f"获取到 {len(query_suggestions) if query_suggestions else 0} 个推荐词:{query_suggestions}")
-
-    if not query_suggestions:
-        # 记录到历史
-        context.operations_history.append({
-            "operation_type": "get_query_suggestions",
-            "timestamp": datetime.now().isoformat(),
-            "query": query,
-            "suggestions": [],
-            "evaluations": "未返回任何推荐词",
-        })
-        return []
-
-    # 2. 并发评估所有推荐词
-    async def evaluate_single_query(q_sug: str):
-        eval_input = f"""
-<需求上下文>
-{context.q_context}
-</需求上下文>
-
-<原始query问题>
-{context.q}
-</原始query问题>
-
-<平台sug词条>
-{q_sug}
-</平台sug词条>
-
-请对该sug词条进行三层评估(一次性返回所有层级的评分):
-
-第一层:判断sug词条是否为知识需求(knowledge_recognition: 0或1)
-第二层:评估知识动机匹配度(motivation_score: 0-1,需>=0.9)
-第三层:评估相关性得分(relevance_score: 0-1,>=0.9为高度满足)
-
-重要:即使第一层=0或第二层<0.9,也要完整计算所有层级的得分。
-"""
-        evaluator_result = await Runner.run(evaluator, eval_input)
-        result: EvaluationFeedback = evaluator_result.final_output
-        return {
-            "query": q_sug,
-            "knowledge_recognition": result.knowledge_recognition,
-            "knowledge_recognition_reason": result.knowledge_recognition_reason,
-            "motivation_score": result.motivation_score,
-            "motivation_breakdown": result.motivation_breakdown.model_dump(),
-            "relevance_score": result.relevance_score,
-            "relevance_breakdown": result.relevance_breakdown.model_dump(),
-            "overall_reason": result.overall_reason,
-        }
-
-    evaluations = await asyncio.gather(*[evaluate_single_query(q_sug) for q_sug in query_suggestions])
-
-    # 3. 记录到历史
-    context.operations_history.append({
-        "operation_type": "get_query_suggestions",
-        "timestamp": datetime.now().isoformat(),
-        "query": query,
-        "suggestions": query_suggestions,
-        "evaluations": evaluations,
-    })
-
-    return evaluations
-
-
-async def generate_modification_strategy(
-    current_query: str,
-    evaluations: list[dict],
-    annotation: str,
-    context: RunContext
-) -> ModificationStrategy:
-    """生成修改策略"""
-    print("\n正在生成修改策略...")
-
-    # 整理历史尝试记录 - 完整保留推荐词和评估结果
-    history_records = []
-    round_num = 0
-
-    for op in context.operations_history:
-        if op["operation_type"] == "get_query_suggestions":
-            round_num += 1
-            record = {
-                "round": round_num,
-                "query": op["query"],
-                "suggestions": op["suggestions"],
-                "evaluations": op["evaluations"]
-            }
-            history_records.append(record)
-        elif op["operation_type"] == "modify_query":
-            # 修改操作也记录,但不增加轮数
-            history_records.append({
-                "operation": "modify_query",
-                "strategy_type": op.get("strategy_type", op.get("modification_type")),  # 兼容旧字段
-                "operation_type_detail": op.get("operation_type_detail"),  # 新增:具体操作类型(增加、删减、调序、替换)
-                "base_query": op.get("base_query"),
-                "base_query_source": op.get("base_query_source"),
-                "modification_actions": op.get("modification_actions", []),
-                "original_query": op["original_query"],
-                "new_query": op["new_query"],
-                "reasoning": op["reasoning"]
-            })
-
-    # 格式化历史记录为JSON
-    history_json = json.dumps(history_records, ensure_ascii=False, indent=2)
-
-    strategy_input = f"""
-<原始问题标注(三层)>
-{annotation}
-</原始问题标注(三层)>
-
-<历史尝试记录(完整)>
-{history_json}
-</历史尝试记录(完整)>
-
-<当前query>
-{current_query}
-</当前query>
-
-<当前轮推荐词评估结果>
-{json.dumps(evaluations, ensure_ascii=False, indent=2) if evaluations else "空列表"}
-</当前轮推荐词评估结果>
-
-请基于所有历史尝试和当前评估结果,生成下一步的query修改策略。
-
-重点分析:
-
-1. **当前推荐词的信号**:
-   - 系统理解成什么主题了?(旅游?教程?素材?工具?品种?)
-   - 推荐词揭示了什么信息?系统在哪个方向有内容?
-   - **不要只看essence_score**:essence=0的推荐词也可能是好的探索起点
-   - 哪个推荐词最有潜力作为下一步的桥梁?
-
-2. **历史趋势分析**:
-   - 推荐词的主题变化:从"品种介绍"→"旅游"→"摄影"?
-   - 是否在逐步接近目标?还是原地打转(主题不变)?
-   - 哪些query让系统理解方向改变了?
-
-3. **确定探索目的**:
-   - 下一步query的探索目的是什么?
-     * 验证系统对某个词的理解?
-     * 往某个方向引导系统?
-     * 利用推荐词作为桥梁?
-   - **记住**:中间query不需要满足原需求,关键是达成探索目的
-"""
-    result = await Runner.run(strategy_generator, strategy_input)
-    strategy: ModificationStrategy = result.final_output
-    return strategy
-
-
-def find_qualified_queries(evaluations: list[dict]) -> dict:
-    """分级查找合格query
-
-    Returns:
-        {
-            "highly_qualified": [...],      # 高度满足:knowledge=1, motivation>=0.9, relevance>=0.9
-            "moderately_qualified": [...],  # 中高满足:knowledge=1, motivation>=0.9, relevance>=0.7
-            "lower_qualified": [...],       # 中低满足:knowledge=1, motivation>=0.9, relevance>=0.4
-        }
-    """
-    # 高度满足:knowledge=1, motivation>=0.9, relevance>=0.9
-    highly_qualified = [
-        e for e in evaluations
-        if e['knowledge_recognition'] == 1
-        and e['motivation_score'] >= 0.9
-        and e['relevance_score'] >= 0.9
-    ]
-
-    # 中高满足:knowledge=1, motivation>=0.9, 0.7<=relevance<0.9
-    moderately_qualified = [
-        e for e in evaluations
-        if e['knowledge_recognition'] == 1
-        and e['motivation_score'] >= 0.9
-        and 0.7 <= e['relevance_score'] < 0.9
-    ]
-
-    # 中低满足:knowledge=1, motivation>=0.9, 0.4<=relevance<0.7
-    lower_qualified = [
-        e for e in evaluations
-        if e['knowledge_recognition'] == 1
-        and e['motivation_score'] >= 0.9
-        and 0.4 <= e['relevance_score'] < 0.7
-    ]
-
-    return {
-        "highly_qualified": sorted(highly_qualified, key=lambda x: x['relevance_score'], reverse=True),
-        "moderately_qualified": sorted(moderately_qualified, key=lambda x: x['relevance_score'], reverse=True),
-        "lower_qualified": sorted(lower_qualified, key=lambda x: x['relevance_score'], reverse=True),
-    }
-
-
-# ============================================================================
-# 主流程(代码控制)
-# ============================================================================
-
-async def optimize_query(context: RunContext, max_rounds: int = 20) -> dict:
-    """
-    主优化流程 - 由代码控制
-
-    Args:
-        context: 运行上下文
-        max_rounds: 最大迭代轮数,默认20
-
-    返回格式:
-    {
-        "success": True/False,
-        "result": {...} or None,
-        "message": "..."
-    }
-    """
-    # 1. 标注问题(仅一次)
-    annotation = await annotate_question(context.q_with_context)
-    context.question_annotation = annotation
-
-    # 2. 迭代优化
-    current_query = context.q
-
-    for round_num in range(1, max_rounds + 1):
-        print(f"\n{'='*60}")
-        print(f"第 {round_num} 轮:{'使用原始问题' if round_num == 1 else '使用修改后的query'}")
-        print(f"当前query: {current_query}")
-        print(f"{'='*60}")
-
-        # 获取推荐词并评估
-        evaluations = await get_suggestions_with_eval(current_query, annotation, context)
-
-        if evaluations:
-            # 检查是否找到合格query(分级筛选)
-            qualified = find_qualified_queries(evaluations)
-
-            # 优先返回高度满足的query
-            if qualified["highly_qualified"]:
-                return {
-                    "success": True,
-                    "level": "highly_qualified",
-                    "results": qualified["highly_qualified"],
-                    "message": f"第{round_num}轮找到{len(qualified['highly_qualified'])}个高度满足的query(知识=1, 动机≥0.9, 相关性≥0.9)"
-                }
-
-            # 其次返回中高满足的query
-            if qualified["moderately_qualified"]:
-                return {
-                    "success": True,
-                    "level": "moderately_qualified",
-                    "results": qualified["moderately_qualified"],
-                    "message": f"第{round_num}轮找到{len(qualified['moderately_qualified'])}个中高满足的query(知识=1, 动机≥0.9, 相关性≥0.7)"
-                }
-
-        # 如果是最后一轮,不再生成策略
-        if round_num == max_rounds:
-            break
-
-        # 生成修改策略
-        print(f"\n--- 生成修改策略 ---")
-        strategy = await generate_modification_strategy(current_query, evaluations, annotation, context)
-
-        print(f"\n修改策略:")
-        print(f"  推理过程:{strategy.reasoning}")
-        print(f"  策略类型:{strategy.strategy_type}")
-        print(f"  操作类型:{strategy.operation_type}")
-        print(f"  基础query:{strategy.base_query} (来源: {strategy.base_query_source})")
-        print(f"  修改动作:{', '.join(strategy.modification_actions)}")
-        print(f"  新query:{strategy.new_query}")
-
-        # 记录修改
-        context.operations_history.append({
-            "operation_type": "modify_query",
-            "timestamp": datetime.now().isoformat(),
-            "reasoning": strategy.reasoning,
-            "strategy_type": strategy.strategy_type,
-            "operation_type_detail": strategy.operation_type,
-            "base_query": strategy.base_query,
-            "base_query_source": strategy.base_query_source,
-            "modification_actions": strategy.modification_actions,
-            "original_query": current_query,
-            "new_query": strategy.new_query,
-        })
-
-        # 更新当前query
-        current_query = strategy.new_query
-
-    # 所有轮次后仍未找到高度/中高满足的,降低标准查找
-    print(f"\n{'='*60}")
-    print(f"{max_rounds}轮后未找到高度/中高满足的query,降低标准(相关性 >= 0.4)")
-    print(f"{'='*60}")
-
-    qualified = find_qualified_queries(evaluations)
-    if qualified["lower_qualified"]:
-        return {
-            "success": True,
-            "level": "lower_qualified",
-            "results": qualified["lower_qualified"],
-            "message": f"{max_rounds}轮后找到{len(qualified['lower_qualified'])}个中低满足的query(知识=1, 动机≥0.9, 相关性≥0.4)"
-        }
-
-    # 完全失败:找出最接近的(只满足知识识别,但动机不够)
-    knowledge_ok = [
-        e for e in evaluations
-        if e['knowledge_recognition'] == 1
-    ]
-    if knowledge_ok:
-        # 返回所有满足knowledge的,按motivation_score降序
-        closest_queries = sorted(knowledge_ok, key=lambda x: x['motivation_score'], reverse=True)
-        return {
-            "success": False,
-            "level": "failed",
-            "results": closest_queries[:5],  # 只返回前5个
-            "message": f"未找到合格query,但有{len(closest_queries)}个是知识需求(knowledge=1,但motivation<0.9)"
-        }
-
-    return {
-        "success": False,
-        "level": "failed",
-        "results": [],
-        "message": "未找到任何知识类推荐词(所有推荐词的knowledge_recognition均为0)"
-    }
-
-
-# ============================================================================
-# 输出格式化
-# ============================================================================
-
-def format_output(optimization_result: dict, context: RunContext) -> str:
-    """格式化输出结果"""
-    results = optimization_result.get("results", [])
-    level = optimization_result.get("level", "")
-
-    # 满足程度映射
-    level_map = {
-        "highly_qualified": "高度满足 (90-100%)",
-        "moderately_qualified": "中高满足 (70-89%)",
-        "lower_qualified": "中低满足 (40-69%)",
-        "failed": "未通过"
-    }
-
-    if optimization_result["success"] and results:
-        output = f"原始问题:{context.q}\n"
-        output += f"满足程度:{level_map.get(level, '未知')}\n"
-        output += f"状态:{optimization_result['message']}\n\n"
-        output += "推荐query(按相关性降序):\n"
-        for i, result in enumerate(results, 1):
-            output += f"\n{i}. {result['query']}\n"
-            output += f"   - 知识识别:{'是' if result['knowledge_recognition'] == 1 else '否'} ({result['knowledge_recognition_reason'][:50]}...)\n"
-            output += f"   - 动机匹配度:{result['motivation_score']:.2f} (≥0.9通过)\n"
-            output += f"     * 核心意图动词: {result['motivation_breakdown'].get('核心意图动词', 0):.2f}\n"
-            output += f"     * 目标对象: {result['motivation_breakdown'].get('目标对象', 0):.2f}\n"
-            output += f"     * 终极目的: {result['motivation_breakdown'].get('终极目的', 0):.2f}\n"
-            output += f"   - 相关性得分:{result['relevance_score']:.2f} (≥0.9高度满足)\n"
-            output += f"     * 核心动作: {result['relevance_breakdown'].get('核心动作', 0):.2f}\n"
-            output += f"     * 目标对象: {result['relevance_breakdown'].get('目标对象', 0):.2f}\n"
-            output += f"     * 使用场景: {result['relevance_breakdown'].get('使用场景', 0):.2f}\n"
-            output += f"     * 终极目的: {result['relevance_breakdown'].get('终极目的', 0):.2f}\n"
-            output += f"   - 综合评估:{result['overall_reason'][:100]}...\n"
-        return output.strip()
-    else:
-        output = f"原始问题:{context.q}\n"
-        output += f"结果:未找到合格推荐query\n"
-        output += f"满足程度:{level_map.get(level, '未知')}\n"
-        output += f"原因:{optimization_result['message']}\n"
-
-        if results:
-            output += "\n最接近的推荐词:\n"
-            for i, result in enumerate(results[:3], 1):  # 只显示前3个
-                output += f"\n{i}. {result['query']}\n"
-                output += f"   - 知识识别:{'是' if result['knowledge_recognition'] == 1 else '否'}\n"
-                output += f"   - 动机匹配度:{result['motivation_score']:.2f}\n"
-                output += f"   - 相关性得分:{result['relevance_score']:.2f}\n"
-                output += f"   - 综合评估:{result['overall_reason'][:100]}...\n"
-
-        output += "\n建议:尝试简化问题或调整需求描述"
-        return output.strip()
-
-
-# ============================================================================
-# 主函数
-# ============================================================================
-
-async def main(input_dir: str, max_rounds: int = 20):
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]
-
-    # 日志保存目录
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-
-    # 执行优化流程(代码控制)
-    optimization_result = await optimize_query(run_context, max_rounds=max_rounds)
-
-    # 格式化输出
-    final_output = format_output(optimization_result, run_context)
-    print(f"\n{'='*60}")
-    print("最终结果")
-    print(f"{'='*60}")
-    print(final_output)
-
-    # 保存结果
-    run_context.optimization_result = optimization_result
-    run_context.final_output = final_output
-
-    # 保存 RunContext 到 log_dir
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    parser.add_argument(
-        "--max-rounds",
-        type=int,
-        default=20,
-        help="最大迭代轮数,默认: 20"
-    )
-    args = parser.parse_args()
-
-    asyncio.run(main(args.input_dir, max_rounds=args.max_rounds))

+ 0 - 765
sug_v5_1.py

@@ -1,765 +0,0 @@
-import asyncio
-import json
-import os
-import argparse
-from datetime import datetime
-
-from agents import Agent, Runner
-from lib.my_trace import set_trace
-from typing import Literal
-from pydantic import BaseModel, Field
-
-from lib.utils import read_file_as_string
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-    question_annotation: str | None = Field(default=None, description="问题的标注结果")
-    operations_history: list[dict] = Field(default_factory=list, description="记录所有操作的历史")
-    final_output: str | None = Field(default=None, description="最终输出结果")
-
-
-# ============================================================================
-# Agent 1: 问题标注专家
-# ============================================================================
-question_annotation_instructions = """
-你是搜索需求分析专家。给定问题(含需求背景),在原文上标注三层:本质、硬性、软性。
-
-## 判断标准
-
-**[本质]** - 问题的核心意图
-- 如何获取、教程、推荐、作品、测评等
-
-**[硬]** - 客观事实性约束(可明确验证、非主观判断)
-- 能明确区分类别的:地域、时间、对象、工具、操作类型
-- 特征:改变后得到完全不同类别的结果
-
-**[软]** - 主观判断性修饰(因人而异、程度性的)
-- 需要主观评价的:质量、速度、美观、特色、程度
-- 特征:改变后仍是同类结果,只是满足程度不同
-
-## 输出格式
-
-词语[本质-描述]、词语[硬-描述]、词语[软-描述]
-
-## 注意
-- 只输出标注后的字符串
-- 结合需求背景判断意图
-""".strip()
-
-question_annotator = Agent[None](
-    name="问题标注专家",
-    instructions=question_annotation_instructions,
-)
-
-
-# ============================================================================
-# Agent 2: 评估专家
-# ============================================================================
-eval_instructions = """
-你是搜索query评估专家。给定原始问题标注(三层)和推荐query,评估三个分数。
-
-## 评估目标
-
-用这个推荐query搜索,能否找到满足原始需求的内容?
-
-## 三层评分
-
-### 1. essence_score(本质/意图)= 0 或 1
-
-推荐query的本质/意图是否与原问题一致?
-
-**原问题标注中的[本质-XXX]对应推荐词要求:**
-- 找方法/如何获取 → 推荐词**必须明确包含方法/途径类词汇**
-  - ✅ "...网站推荐"、"如何获取..."、"...获取途径"、"...方法"
-  - ❌ "...下载"、"...素材"(直接找内容,不是找方法)
-- 教程/学习 → 推荐词应该是教程/教学
-- 作品/欣赏 → 推荐词应该是作品展示
-- 工具/推荐 → 推荐词应该是工具推荐
-
-**评分原则:**
-- 1 = 本质一致,推荐词**明确表达**相同意图
-- 0 = 本质改变或**不够明确**(宁可严格,不可放松)
-
-### 2. hard_score(硬性约束)= 0 或 1
-
-在本质一致的前提下,是否满足所有硬性约束?
-
-**原问题标注中的[硬-XXX]:**地域、时间、对象、质量、工具等
-
-**评分:**
-- 1 = 所有硬性约束都满足
-- 0 = 任一硬性约束不满足
-
-### 3. soft_score(软性修饰)= 0-1
-
-软性修饰词保留了多少?
-
-**评分参考:**
-- 1.0 = 完整保留
-- 0.7-0.9 = 保留核心
-- 0.4-0.6 = 部分丢失
-- 0-0.3 = 大量丢失
-
-## 注意
-
-- essence=0 直接拒绝,不管hard/soft多高
-- essence=1, hard=0 也要拒绝
-- essence=1, hard=1 才看soft_score
-""".strip()
-
-class EvaluationFeedback(BaseModel):
-    """评估反馈模型 - 三层评分"""
-    essence_score: Literal[0, 1] = Field(..., description="本质/意图匹配度,0或1")
-    hard_score: Literal[0, 1] = Field(..., description="硬性约束匹配度,0或1")
-    soft_score: float = Field(..., description="软性修饰完整度,0-1")
-    reason: str = Field(..., description="评估理由")
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_instructions,
-    output_type=EvaluationFeedback,
-)
-
-
-# ============================================================================
-# Agent 3: 修改策略生成专家
-# ============================================================================
-strategy_instructions = """
-你是query修改策略专家。**模拟人在搜索引擎中的真实搜索行为**,基于反馈动态调整query。
-
-## 核心思路:搜索是探索过程,不是直达过程
-
-**关键认知:**
-1. **中间query不需要满足原始需求** - 它是探索工具,可以偏离原需求
-2. **推荐词是最宝贵的反馈信号** - 告诉你系统理解成什么了,有什么内容
-3. **每一步query都有明确的探索目的** - 不是盲目改词,而是试探和引导
-4. **最终目标:找到满足需求的推荐词** - 不是让query本身满足需求
-
-## 人的真实搜索过程
-
-**搜索的本质**:通过多步探索,利用推荐词作为桥梁,逐步引导系统
-
-**典型模式**:
-
-第1步:直接尝试
-- 目的:看系统能否直接理解
-- 结果:空列表或essence=0
-- essence=0的推荐词:告诉你系统理解成什么了
-
-第2步:降低要求,简化query
-- 目的:让系统有响应,看它在基础层面有什么
-- 推荐词虽然essence=0,但揭示了系统在某个主题有内容
-- **关键**:选一个最有潜力的推荐词
-
-第3步:基于推荐词,往目标方向引导
-- 目的:利用推荐词作为桥梁,加上目标方向的词
-- 推荐词还是essence=0,但主题在变化(接近目标)
-- **渐进式**:不求一步到位,每步都有进展
-
-第4步:继续引导或换角度
-- 如果推荐词主题不变 → 换角度
-- 如果推荐词主题在接近 → 继续引导
-
-最终:找到essence=1的推荐词
-
-**关键原则**:
-1. essence_score是评估推荐词的,不是评估中间query的
-2. essence=0的推荐词也有价值,它揭示了系统的理解方向
-3. 每一步都有明确的探索目的,看目的是否达成
-4. 通过推荐词的主题变化,判断是否在接近目标
-
-## 输入信息
-- 原始问题标注(三层):本质、硬性约束、软性修饰
-- 历史尝试记录:所有轮次的query、推荐词、评估结果
-- 当前query和推荐词评估
-
-## 分析步骤
-
-### 第一步:理解当前推荐词的信号
-**核心问题:推荐词告诉我什么信息?**
-
-**重要提醒:essence_score是评估推荐词是否满足原始需求的最终目标**
-- essence_score=1: 推荐词满足原需求的本质
-- essence_score=0: 推荐词不满足原需求的本质
-- **但中间query的目的可能不是满足原需求**,所以essence_score只是参考
-
-1. **系统理解层面**(看推荐词的主题):
-   - 空列表 → 系统完全不理解当前query
-   - 有推荐词 → 系统理解成了什么主题?
-     - 旅游?教程?素材?工具?品种介绍?
-     - 这些主题是否有助于往目标方向引导?
-
-2. **内容可用性层面**(看推荐词的价值):
-   - **即使推荐词essence=0,也可能是很好的探索起点**
-   - 例如:推荐词"川西旅游攻略"虽然essence=0,但揭示了系统认识"川西"
-   - 哪些推荐词最有潜力作为下一步的桥梁?
-
-3. **探索目的验证**:
-   - 当前query的探索目的是什么?达到了吗?
-   - 例如:目的是"看系统对川西有什么" → 达到了(有推荐词)
-   - 下一步要验证/探索什么?
-
-### 第二步:回顾历史,识别规律
-
-**什么是真正的死胡同?**
-- ❌ 不是essence=0就是死胡同
-- ✅ 死胡同:连续多次尝试,**推荐词的主题完全不变**
-  - 例如:连续3轮都是"取名/品种介绍"
-  - 例如:连续3轮都是"旅游攻略"
-
-**什么是有进展?**
-- ✅ 推荐词的主题在变化(旅游→摄影→作品→素材)
-- ✅ 即使essence=0,也说明在接近目标
-- ✅ 说明系统的理解在被引导
-
-**避免同义词打转**:
-- ❌ "素材"→"照片"→"图片"→"资源"
-- 这些同义词不会改变系统理解,纯粹浪费轮次
-- ✅ 应该从推荐词中选一个作为桥梁,或换完全不同的角度
-
-### 第三步:选择策略类型(带着明确的探索目的)
-
-**refine_current(微调当前query)**
-- 适用:推荐词方向对了,需要微调让它更精确
-- 探索目的:在正确方向上精细化
-- 动作:加词/减词/换词/调整顺序
-
-**use_recommendation(选推荐词作为新起点)** ⭐ 最重要策略
-- 适用:推荐词虽然essence=0,但**揭示了系统在这个方向有内容**
-- 探索目的:利用推荐词这个客观信号,引导系统往目标方向
-- **核心思维**:推荐词是系统给你的提示,告诉你"我有这个"
-- 动作:
-  - 选一个最有潜力的推荐词作为base_query
-  - 在它基础上加目标方向的词
-  - **这个新query可能不满足原需求,但目的是探索和引导**
-
-**change_approach(换完全不同的角度)**
-- 适用:当前方向是死路(多次尝试推荐词主题不变)
-- 探索目的:跳出当前框架,从另一个角度切入
-- 动作:换一种完全不同的表述方式
-
-**relax_constraints(放宽约束)**
-- 适用:query太复杂,系统不理解(返回空列表)
-- 探索目的:先让系统有响应,看它在最基础层面有什么
-- 动作:去掉限定词,保留核心概念
-
-## 输出要求
-
-### 1. reasoning(推理过程)
-必须包含三部分,**核心是目标导向**:
-
-- **当前位置评估**(相对于最终目标):
-  - 当前推荐词反映了系统在什么主题空间?
-  - 离原始需求多远?(完全偏离/部分相关/已经接近)
-  - **不要只看essence_score**:
-    - essence_score=0不代表没用,可能是必经之路
-    - 关键看推荐词能否帮助靠近目标
-
-- **历史进展判断**:
-  - 推荐词主题是否在变化?(变化=进展,不变=死胡同)
-  - 在靠近目标,还是原地打转?
-  - 哪些尝试让系统理解发生了改变?
-
-- **下一步探索规划**:
-  - **这一步的作用**(必须明确!):
-    * 离目标远?→ 先让系统理解某个关键词/概念
-    * 部分相关?→ 选推荐词作为桥梁,引导往目标方向
-    * 已经接近?→ 微调细节,精确匹配
-  - 为什么选这个base_query?
-  - 这个修改如何让我们靠近目标?
-  - **重要**:中间query不需要满足原需求,只要能靠近目标
-
-### 2. strategy_type
-从4种策略中选择:refine_current, use_recommendation, change_approach, relax_constraints
-
-### 3. base_query
-**关键**:可以选择历史中的query,也可以选择历史推荐词
-- 如果选历史query:base_query_source = "history_query"
-- 如果选历史推荐词:base_query_source = "history_recommendation"
-
-### 4. base_query_source
-说明base_query的来源
-
-### 5. modification_action
-**重要:一次只做一个核心动作**
-- 不要列举多个动作
-- 只描述最核心的那一个修改
-- 例如:"选择推荐词'川西旅游'作为新起点"
-- 例如:"去掉'如何获取'改为直接搜内容"
-- 例如:"加上'AI生成'转向生成方向"
-
-### 6. new_query
-最终的新query
-
-## 重要原则
-
-1. **推荐词是最宝贵的反馈** - 充分利用推荐词这个客观信号
-   - 即使essence=0的推荐词,也揭示了系统在这个方向有什么
-   - **优先考虑use_recommendation策略** - 选一个推荐词作为起点
-
-2. **中间query可以偏离原需求** - 每一步都有明确的探索目的
-   - 不要纠结"这个query不满足原需求"
-   - 关键是:这个query能不能帮你往正确方向引导系统
-
-3. **识别死胡同,及时换方向**
-   - 如果多次尝试推荐词主题不变 → 换方向
-   - 如果推荐词越来越偏 → 回退到之前的某个好的起点
-
-4. **保持推理简洁** - 抓住关键信息
-   - 明确说出探索目的
-   - 不要重复啰嗦
-""".strip()
-
-class ModificationStrategy(BaseModel):
-    """修改策略模型 - 模拟人的搜索调整过程"""
-    reasoning: str = Field(..., description="推理过程:1)当前推荐词分析:系统理解成什么了?2)历史尝试总结:哪些方向有效/无效?3)下一步策略:为什么这样调整?")
-
-    strategy_type: Literal[
-        "refine_current",      # 微调当前query(加词/减词/换词/换顺序)
-        "use_recommendation",  # 选择推荐词作为新起点,在它基础上修改
-        "change_approach",     # 换完全不同的表述角度
-        "relax_constraints"    # 放宽约束,去掉部分限定词
-    ] = Field(..., description="策略类型")
-
-    base_query: str = Field(..., description="基础query,可以是:1)历史中的query 2)历史推荐词中的某一个")
-    base_query_source: Literal["history_query", "history_recommendation"] = Field(..., description="base_query的来源")
-
-    modification_action: str = Field(..., description="核心修改动作(只一个),如:'选择推荐词作为新起点' 或 '去掉方法类词改为直接搜内容' 或 '加上AI生成转向生成方向'")
-
-    new_query: str = Field(..., description="修改后的新query")
-
-strategy_generator = Agent[None](
-    name="策略生成专家",
-    instructions=strategy_instructions,
-    output_type=ModificationStrategy,
-)
-
-
-# ============================================================================
-# 核心函数
-# ============================================================================
-
-async def annotate_question(q_with_context: str) -> str:
-    """标注问题(三层)"""
-    print("\n正在标注问题...")
-    result = await Runner.run(question_annotator, q_with_context)
-    annotation = str(result.final_output)
-    print(f"问题标注完成:{annotation}")
-    return annotation
-
-
-async def get_suggestions_with_eval(query: str, annotation: str, context: RunContext) -> list[dict]:
-    """获取推荐词并评估"""
-    print(f"\n正在获取推荐词:{query}")
-
-    # 1. 调用小红书API
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-    query_suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-    print(f"获取到 {len(query_suggestions) if query_suggestions else 0} 个推荐词:{query_suggestions}")
-
-    if not query_suggestions:
-        # 记录到历史
-        context.operations_history.append({
-            "operation_type": "get_query_suggestions",
-            "timestamp": datetime.now().isoformat(),
-            "query": query,
-            "suggestions": [],
-            "evaluations": "未返回任何推荐词",
-        })
-        return []
-
-    # 2. 并发评估所有推荐词
-    async def evaluate_single_query(q_sug: str):
-        eval_input = f"""
-<需求背景>
-{context.q_context if context.q_context else "无"}
-</需求背景>
-
-<原始问题>
-{context.q}
-</原始问题>
-
-<原始问题标注(三层)>
-{annotation}
-</原始问题标注(三层)>
-
-<待评估的推荐query>
-{q_sug}
-</待评估的推荐query>
-
-请评估该推荐query的三个分数:
-1. essence_score: 本质/意图是否一致(0或1)
-2. hard_score: 硬性约束是否满足(0或1)
-3. soft_score: 软性修饰保留程度(0-1)
-4. reason: 详细的评估理由
-"""
-        evaluator_result = await Runner.run(evaluator, eval_input)
-        result: EvaluationFeedback = evaluator_result.final_output
-        return {
-            "query": q_sug,
-            "essence_score": result.essence_score,
-            "hard_score": result.hard_score,
-            "soft_score": result.soft_score,
-            "reason": result.reason,
-        }
-
-    evaluations = await asyncio.gather(*[evaluate_single_query(q_sug) for q_sug in query_suggestions])
-
-    # 3. 记录到历史
-    context.operations_history.append({
-        "operation_type": "get_query_suggestions",
-        "timestamp": datetime.now().isoformat(),
-        "query": query,
-        "suggestions": query_suggestions,
-        "evaluations": evaluations,
-    })
-
-    return evaluations
-
-
-async def generate_modification_strategy(
-    annotation: str,
-    context: RunContext
-) -> ModificationStrategy:
-    """生成修改策略"""
-    print("\n正在生成修改策略...")
-
-    # 整理历史尝试记录 - 完整保留推荐词和评估结果
-    history_records = []
-    round_num = 0
-
-    for op in context.operations_history:
-        if op["operation_type"] == "get_query_suggestions":
-            round_num += 1
-            record = {
-                "round": round_num,
-                "query": op["query"],
-                "suggestions": op["suggestions"],
-                "evaluations": op["evaluations"]
-            }
-            history_records.append(record)
-        elif op["operation_type"] == "modify_query":
-            # 修改操作也记录,但不增加轮数
-            history_records.append({
-                "operation": "modify_query",
-                "strategy_type": op.get("strategy_type", op.get("modification_type")),  # 兼容旧字段
-                "base_query": op.get("base_query"),
-                "base_query_source": op.get("base_query_source"),
-                "modification_action": op.get("modification_action", op.get("modification_actions", [])),  # 兼容旧版本
-                "original_query": op["original_query"],
-                "new_query": op["new_query"],
-                "reasoning": op["reasoning"]
-            })
-
-    # 格式化历史记录为JSON
-    history_json = json.dumps(history_records, ensure_ascii=False, indent=2)
-
-    strategy_input = f"""
-<需求背景>
-{context.q_context if context.q_context else "无"}
-</需求背景>
-
-<原始问题>
-{context.q}
-</原始问题>
-
-<原始问题标注(三层)>
-{annotation}
-</原始问题标注(三层)>
-
-<历史尝试记录(完整)>
-{history_json}
-</历史尝试记录(完整)>
-
-请基于所有历史尝试,生成下一步的query修改策略。
-
-**说明**:历史记录中最后一条就是当前轮的query和推荐词评估结果。
-
-**核心思路**:每一步都要明确 "当前在哪 → 离目标多远 → 下一步做什么能靠近目标"
-
-重点分析:
-
-1. **评估当前位置**(相对于最终目标):
-   - 当前推荐词反映了什么?系统在哪个主题空间?
-   - 离原始需求的距离:完全偏离?部分相关?已经接近?
-   - **不要只看essence_score**:essence=0不代表没用,可能是通往目标的必经之路
-
-2. **判断历史进展**:
-   - 推荐词主题是否在变化?(变化=有进展,不变=死胡同)
-   - 是在靠近目标,还是在原地打转?
-   - 哪个方向的query让系统理解发生了改变?
-
-3. **规划下一步探索**:
-   - **这一步query的作用是什么**?(必须明确!)
-     * 如果离目标很远:需要先让系统理解某个关键词/概念
-     * 如果部分相关:选一个推荐词作为桥梁,在它基础上引导
-     * 如果已经接近:微调细节,精确匹配需求
-   - **记住**:中间query不需要满足原需求,只要能让我们往目标靠近
-"""
-    result = await Runner.run(strategy_generator, strategy_input)
-    strategy: ModificationStrategy = result.final_output
-    return strategy
-
-
-def find_qualified_queries(evaluations: list[dict], min_soft_score: float = 0.7) -> list[dict]:
-    """查找所有合格的query,按soft_score降序排列"""
-    qualified = [
-        e for e in evaluations
-        if e['essence_score'] == 1
-        and e['hard_score'] == 1
-        and e['soft_score'] >= min_soft_score
-    ]
-    # 按soft_score降序排列
-    return sorted(qualified, key=lambda x: x['soft_score'], reverse=True)
-
-
-# ============================================================================
-# 主流程(代码控制)
-# ============================================================================
-
-async def optimize_query(context: RunContext, max_rounds: int = 20) -> dict:
-    """
-    主优化流程 - 由代码控制
-
-    Args:
-        context: 运行上下文
-        max_rounds: 最大迭代轮数,默认20
-
-    返回格式:
-    {
-        "success": True/False,
-        "result": {...} or None,
-        "message": "..."
-    }
-    """
-    # 1. 标注问题(仅一次)
-    annotation = await annotate_question(context.q_with_context)
-    context.question_annotation = annotation
-
-    # 2. 迭代优化
-    current_query = context.q
-
-    for round_num in range(1, max_rounds + 1):
-        print(f"\n{'='*60}")
-        print(f"第 {round_num} 轮:{'使用原始问题' if round_num == 1 else '使用修改后的query'}")
-        print(f"当前query: {current_query}")
-        print(f"{'='*60}")
-
-        # 获取推荐词并评估
-        evaluations = await get_suggestions_with_eval(current_query, annotation, context)
-
-        if evaluations:
-            # 检查是否找到合格query
-            qualified_queries = find_qualified_queries(evaluations, min_soft_score=0.7)
-            if qualified_queries:
-                return {
-                    "success": True,
-                    "results": qualified_queries,
-                    "message": f"第{round_num}轮找到{len(qualified_queries)}个合格query"
-                }
-
-        # 如果是最后一轮,不再生成策略
-        if round_num == max_rounds:
-            break
-
-        # 生成修改策略
-        print(f"\n--- 生成修改策略 ---")
-        strategy = await generate_modification_strategy(annotation, context)
-
-        print(f"\n修改策略:")
-        print(f"  推理过程:{strategy.reasoning}")
-        print(f"  策略类型:{strategy.strategy_type}")
-        print(f"  基础query:{strategy.base_query} (来源: {strategy.base_query_source})")
-        print(f"  修改动作:{strategy.modification_action}")
-        print(f"  新query:{strategy.new_query}")
-
-        # 记录修改
-        context.operations_history.append({
-            "operation_type": "modify_query",
-            "timestamp": datetime.now().isoformat(),
-            "reasoning": strategy.reasoning,
-            "strategy_type": strategy.strategy_type,
-            "base_query": strategy.base_query,
-            "base_query_source": strategy.base_query_source,
-            "modification_action": strategy.modification_action,
-            "original_query": current_query,
-            "new_query": strategy.new_query,
-        })
-
-        # 更新当前query
-        current_query = strategy.new_query
-
-    # 所有轮次后仍未找到,从所有历史评估中降低标准查找
-    print(f"\n{'='*60}")
-    print(f"{max_rounds}轮后未找到最优query,降低标准(soft_score >= 0.5)")
-    print(f"{'='*60}")
-
-    # 收集所有历史轮次的评估结果
-    all_evaluations = []
-    for op in context.operations_history:
-        if op["operation_type"] == "get_query_suggestions" and op["evaluations"]:
-            all_evaluations.extend(op["evaluations"])
-
-    if not all_evaluations:
-        return {
-            "success": False,
-            "results": [],
-            "message": "所有轮次均未返回推荐词"
-        }
-
-    # 降级查找:soft_score >= 0.5
-    acceptable_queries = find_qualified_queries(all_evaluations, min_soft_score=0.5)
-    if acceptable_queries:
-        return {
-            "success": True,
-            "results": acceptable_queries,
-            "message": f"{max_rounds}轮后找到{len(acceptable_queries)}个可接受query(soft_score >= 0.5)"
-        }
-
-    # 完全失败:找出最接近的(essence=1, hard=1)
-    essence_hard_ok = [
-        e for e in all_evaluations
-        if e['essence_score'] == 1 and e['hard_score'] == 1
-    ]
-    if essence_hard_ok:
-        # 返回所有满足essence和hard的,按soft_score降序
-        closest_queries = sorted(essence_hard_ok, key=lambda x: x['soft_score'], reverse=True)
-        return {
-            "success": False,
-            "results": closest_queries,
-            "message": f"未找到合格query,但有{len(closest_queries)}个接近的推荐词(essence=1, hard=1)"
-        }
-
-    return {
-        "success": False,
-        "results": [],
-        "message": "未找到任何满足本质和硬性约束的推荐词"
-    }
-
-
-# ============================================================================
-# 输出格式化
-# ============================================================================
-
-def format_output(optimization_result: dict, context: RunContext) -> str:
-    """格式化输出结果"""
-    results = optimization_result.get("results", [])
-
-    if optimization_result["success"] and results:
-        output = f"原始问题:{context.q}\n"
-        output += f"状态:{optimization_result['message']}\n\n"
-        output += "合格的推荐query(按soft_score降序):\n"
-        for i, result in enumerate(results, 1):
-            output += f"\n{i}. {result['query']}\n"
-            output += f"   - 本质匹配度:{result['essence_score']} (1=本质一致)\n"
-            output += f"   - 硬性约束匹配度:{result['hard_score']} (1=所有约束满足)\n"
-            output += f"   - 软性修饰完整度:{result['soft_score']:.2f} (0-1)\n"
-            output += f"   - 评估理由:{result['reason']}\n"
-        return output.strip()
-    else:
-        output = f"原始问题:{context.q}\n"
-        output += f"结果:未找到合格推荐query\n"
-        output += f"原因:{optimization_result['message']}\n"
-
-        if results:
-            output += "\n最接近的推荐词(按soft_score降序):\n"
-            for i, result in enumerate(results[:3], 1):  # 只显示前3个
-                output += f"\n{i}. {result['query']}\n"
-                output += f"   - essence_score: {result['essence_score']}\n"
-                output += f"   - hard_score: {result['hard_score']}\n"
-                output += f"   - soft_score: {result['soft_score']:.2f}\n"
-                output += f"   - reason: {result['reason']}\n"
-
-        output += "\n建议:尝试简化问题或调整需求描述"
-        return output.strip()
-
-
-# ============================================================================
-# 主函数
-# ============================================================================
-
-async def main(input_dir: str, max_rounds: int = 20):
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]
-
-    # 日志保存目录
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-
-    # 执行优化流程(代码控制)
-    optimization_result = await optimize_query(run_context, max_rounds=max_rounds)
-
-    # 格式化输出
-    final_output = format_output(optimization_result, run_context)
-    print(f"\n{'='*60}")
-    print("最终结果")
-    print(f"{'='*60}")
-    print(final_output)
-
-    # 保存结果
-    run_context.final_output = final_output
-
-    # 保存 RunContext 到 log_dir
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    parser.add_argument(
-        "--max-rounds",
-        type=int,
-        default=20,
-        help="最大迭代轮数,默认: 20"
-    )
-    args = parser.parse_args()
-
-    asyncio.run(main(args.input_dir, max_rounds=args.max_rounds))

+ 0 - 726
sug_v5_2.py

@@ -1,726 +0,0 @@
-import asyncio
-import json
-import os
-import argparse
-from datetime import datetime
-
-from agents import Agent, Runner
-from lib.my_trace import set_trace
-from typing import Literal
-from pydantic import BaseModel, Field
-
-from lib.utils import read_file_as_string
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-    question_annotation: str | None = Field(default=None, description="问题的标注结果")
-    operations_history: list[dict] = Field(default_factory=list, description="记录所有操作的历史")
-    final_output: str | None = Field(default=None, description="最终输出结果")
-
-
-# ============================================================================
-# Agent 1: 问题标注专家
-# ============================================================================
-question_annotation_instructions = """
-你是搜索需求分析专家。给定问题(含需求背景),在原文上标注三层:本质、硬性、软性。
-
-## 判断标准
-
-**[本质]** - 问题的核心意图
-- 如何获取、教程、推荐、作品、测评等
-
-**[硬]** - 客观事实性约束(可明确验证、非主观判断)
-- 能明确区分类别的:地域、时间、对象、工具、操作类型
-- 特征:改变后得到完全不同类别的结果
-
-**[软]** - 主观判断性修饰(因人而异、程度性的)
-- 需要主观评价的:质量、速度、美观、特色、程度
-- 特征:改变后仍是同类结果,只是满足程度不同
-
-## 输出格式
-
-词语[本质-描述]、词语[硬-描述]、词语[软-描述]
-
-## 注意
-- 只输出标注后的字符串
-- 结合需求背景判断意图
-""".strip()
-
-question_annotator = Agent[None](
-    name="问题标注专家",
-    instructions=question_annotation_instructions,
-)
-
-
-# ============================================================================
-# Agent 2: 评估专家
-# ============================================================================
-eval_instructions = """
-你是搜索query评估专家。给定原始问题标注(三层)和推荐query,评估三个分数。
-
-## 评估目标
-
-用这个推荐query搜索,能否找到满足原始需求的内容?
-
-## 三层评分
-
-### 1. essence_score(本质/意图)= 0 或 1
-
-推荐query的本质/意图是否与原问题一致?
-
-**原问题标注中的[本质-XXX]对应推荐词要求:**
-- 找方法/如何获取 → 推荐词**必须明确包含方法/途径类词汇**
-  - ✅ "...网站推荐"、"如何获取..."、"...获取途径"、"...方法"
-  - ❌ "...下载"、"...素材"(直接找内容,不是找方法)
-- 教程/学习 → 推荐词应该是教程/教学
-- 作品/欣赏 → 推荐词应该是作品展示
-- 工具/推荐 → 推荐词应该是工具推荐
-
-**评分原则:**
-- 1 = 本质一致,推荐词**明确表达**相同意图
-- 0 = 本质改变或**不够明确**(宁可严格,不可放松)
-
-### 2. hard_score(硬性约束)= 0 或 1
-
-在本质一致的前提下,是否满足所有硬性约束?
-
-**原问题标注中的[硬-XXX]:**地域、时间、对象、质量、工具等
-
-**评分:**
-- 1 = 所有硬性约束都满足
-- 0 = 任一硬性约束不满足
-
-### 3. soft_score(软性修饰)= 0-1
-
-软性修饰词保留了多少?
-
-**评分参考:**
-- 1.0 = 完整保留
-- 0.7-0.9 = 保留核心
-- 0.4-0.6 = 部分丢失
-- 0-0.3 = 大量丢失
-
-## 注意
-
-- essence=0 直接拒绝,不管hard/soft多高
-- essence=1, hard=0 也要拒绝
-- essence=1, hard=1 才看soft_score
-""".strip()
-
-class EvaluationFeedback(BaseModel):
-    """评估反馈模型 - 三层评分"""
-    essence_score: Literal[0, 1] = Field(..., description="本质/意图匹配度,0或1")
-    hard_score: Literal[0, 1] = Field(..., description="硬性约束匹配度,0或1")
-    soft_score: float = Field(..., description="软性修饰完整度,0-1")
-    reason: str = Field(..., description="评估理由")
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_instructions,
-    output_type=EvaluationFeedback,
-)
-
-
-# ============================================================================
-# Agent 3: 修改策略生成专家
-# ============================================================================
-strategy_instructions = """
-你是query修改策略专家。**模拟人在搜索引擎中的真实搜索行为**,基于反馈动态调整query。
-
-## 核心思路:搜索是探索过程,不是直达过程
-
-**关键认知:**
-1. **中间query不需要满足原始需求** - 它是探索工具,可以偏离原需求
-2. **推荐词是最宝贵的反馈信号** - 告诉你系统理解成什么了,有什么内容
-3. **每一步query都有明确的探索目的** - 不是盲目改词,而是试探和引导
-4. **最终目标:找到满足需求的推荐词** - 不是让query本身满足需求
-
-## 人的真实搜索过程
-
-**搜索的本质**:通过多步探索,利用推荐词作为桥梁,逐步引导系统
-
-**典型模式**:
-
-第1步:直接尝试
-- 目的:看系统能否直接理解
-- 结果:空列表或essence=0
-- essence=0的推荐词:告诉你系统理解成什么了
-
-第2步:降低要求,简化query
-- 目的:让系统有响应,看它在基础层面有什么
-- 推荐词虽然essence=0,但揭示了系统在某个主题有内容
-- **关键**:选一个最有潜力的推荐词
-
-第3步:基于推荐词,往目标方向引导
-- 目的:利用推荐词作为桥梁,加上目标方向的词
-- 推荐词还是essence=0,但主题在变化(接近目标)
-- **渐进式**:不求一步到位,每步都有进展
-
-第4步:继续引导或换角度
-- 如果推荐词主题不变 → 换角度
-- 如果推荐词主题在接近 → 继续引导
-
-最终:找到essence=1的推荐词
-
-**关键原则**:
-1. essence_score是评估推荐词的,不是评估中间query的
-2. essence=0的推荐词也有价值,它揭示了系统的理解方向
-3. 每一步都有明确的探索目的,看目的是否达成
-4. 通过推荐词的主题变化,判断是否在接近目标
-
-## 输入信息
-- 原始问题标注(三层):本质、硬性约束、软性修饰
-- 历史尝试记录:所有轮次的query、推荐词、评估结果
-- 当前query和推荐词评估
-
-## 分析步骤
-
-### 第一步:理解当前推荐词的信号
-**核心问题:推荐词告诉我什么信息?**
-
-**重要提醒:essence_score是评估推荐词是否满足原始需求的最终目标**
-- essence_score=1: 推荐词满足原需求的本质
-- essence_score=0: 推荐词不满足原需求的本质
-- **但中间query的目的可能不是满足原需求**,所以essence_score只是参考
-
-1. **系统理解层面**(看推荐词的主题):
-   - 空列表 → 系统完全不理解当前query
-   - 有推荐词 → 系统理解成了什么主题?
-     - 旅游?教程?素材?工具?品种介绍?
-     - 这些主题是否有助于往目标方向引导?
-
-2. **内容可用性层面**(看推荐词的价值):
-   - **即使推荐词essence=0,也可能是很好的探索起点**
-   - 例如:推荐词"川西旅游攻略"虽然essence=0,但揭示了系统认识"川西"
-   - 哪些推荐词最有潜力作为下一步的桥梁?
-
-3. **探索目的验证**:
-   - 当前query的探索目的是什么?达到了吗?
-   - 例如:目的是"看系统对川西有什么" → 达到了(有推荐词)
-   - 下一步要验证/探索什么?
-
-### 第二步:回顾历史,识别规律
-- 哪些query让系统理解方向变化了?(从"旅游"变成"摄影")
-- 哪些方向是死路?(多次essence=0且推荐词主题不变)
-- **是否有渐进的改善?**(推荐词越来越接近目标)
-
-### 第三步:选择策略类型(带着明确的探索目的)
-
-**refine_current(微调当前query)**
-- 适用:推荐词方向对了,需要微调让它更精确
-- 探索目的:在正确方向上精细化
-- 动作:加词/减词/换词/调整顺序
-
-**use_recommendation(选推荐词作为新起点)** ⭐ 最重要策略
-- 适用:推荐词虽然essence=0,但**揭示了系统在这个方向有内容**
-- 探索目的:利用推荐词这个客观信号,引导系统往目标方向
-- **核心思维**:推荐词是系统给你的提示,告诉你"我有这个"
-- 动作:
-  - 选一个最有潜力的推荐词作为base_query
-  - 在它基础上加目标方向的词
-  - **这个新query可能不满足原需求,但目的是探索和引导**
-
-**change_approach(换完全不同的角度)**
-- 适用:当前方向是死路(多次尝试推荐词主题不变)
-- 探索目的:跳出当前框架,从另一个角度切入
-- 动作:换一种完全不同的表述方式
-
-**relax_constraints(放宽约束)**
-- 适用:query太复杂,系统不理解(返回空列表)
-- 探索目的:先让系统有响应,看它在最基础层面有什么
-- 动作:去掉限定词,保留核心概念
-
-## 输出要求
-
-### 1. reasoning(推理过程)
-必须包含三部分,**重点写探索目的**:
-
-- **当前推荐词信号分析**:
-  - 系统理解成什么主题了?(旅游?教程?素材?工具?品种?)
-  - 推荐词揭示了什么信息?(系统在哪个方向有内容)
-  - **不要只看essence_score**:
-    - essence_score=0不代表推荐词没用
-    - 关键看推荐词的主题是否有助于引导
-  - 哪个推荐词最有潜力作为下一步的桥梁?
-
-- **历史尝试与趋势**:
-  - 系统理解的主题变化:从"品种介绍"→"旅游"→"摄影"
-  - 是否在逐步接近目标?还是原地打转?
-
-- **下一步策略与探索目的**:
-  - **这一步query的探索目的是什么?**
-    - 验证系统对某个词的理解?
-    - 往某个方向引导?
-    - 利用推荐词作为桥梁?
-  - 为什么选这个base_query?
-  - 为什么这样修改?
-  - **重要**:不要纠结"这个query不满足原需求",关键是它能否达成探索目的
-
-### 2. strategy_type
-从4种策略中选择:refine_current, use_recommendation, change_approach, relax_constraints
-
-### 3. base_query
-**关键**:可以选择历史中的query,也可以选择历史推荐词
-- 如果选历史query:base_query_source = "history_query"
-- 如果选历史推荐词:base_query_source = "history_recommendation"
-
-### 4. base_query_source
-说明base_query的来源
-
-### 5. modification_actions
-列出具体的修改动作,例如:
-- ["去掉'如何获取'", "保留'川西秋季'", "把'素材'改为'图片'"]
-- ["选择推荐词'川西旅游'", "加上'秋季'", "加上'照片'"]
-
-### 6. new_query
-最终的新query
-
-## 重要原则
-
-1. **推荐词是最宝贵的反馈** - 充分利用推荐词这个客观信号
-   - 即使essence=0的推荐词,也揭示了系统在这个方向有什么
-   - **优先考虑use_recommendation策略** - 选一个推荐词作为起点
-
-2. **中间query可以偏离原需求** - 每一步都有明确的探索目的
-   - 不要纠结"这个query不满足原需求"
-   - 关键是:这个query能不能帮你往正确方向引导系统
-
-3. **识别死胡同,及时换方向**
-   - 如果多次尝试推荐词主题不变 → 换方向
-   - 如果推荐词越来越偏 → 回退到之前的某个好的起点
-
-4. **保持推理简洁** - 抓住关键信息
-   - 明确说出探索目的
-   - 不要重复啰嗦
-""".strip()
-
-class ModificationStrategy(BaseModel):
-    """修改策略模型 - 模拟人的搜索调整过程"""
-    reasoning: str = Field(..., description="推理过程:1)当前推荐词分析:系统理解成什么了?2)历史尝试总结:哪些方向有效/无效?3)下一步策略:为什么这样调整?")
-
-    strategy_type: Literal[
-        "refine_current",      # 微调当前query(加词/减词/换词/换顺序)
-        "use_recommendation",  # 选择推荐词作为新起点,在它基础上修改
-        "change_approach",     # 换完全不同的表述角度
-        "relax_constraints"    # 放宽约束,去掉部分限定词
-    ] = Field(..., description="策略类型")
-
-    base_query: str = Field(..., description="基础query,可以是:1)历史中的query 2)历史推荐词中的某一个")
-    base_query_source: Literal["history_query", "history_recommendation"] = Field(..., description="base_query的来源")
-
-    modification_actions: list[str] = Field(..., description="具体修改动作的描述,如:['去掉\"如何获取\"', '保留核心词\"川西秋季\"', '把\"素材\"改为\"图片\"']")
-
-    new_query: str = Field(..., description="修改后的新query")
-
-strategy_generator = Agent[None](
-    name="策略生成专家",
-    instructions=strategy_instructions,
-    output_type=ModificationStrategy,
-)
-
-
-# ============================================================================
-# 核心函数
-# ============================================================================
-
-async def annotate_question(q_with_context: str) -> str:
-    """标注问题(三层)"""
-    print("\n正在标注问题...")
-    result = await Runner.run(question_annotator, q_with_context)
-    annotation = str(result.final_output)
-    print(f"问题标注完成:{annotation}")
-    return annotation
-
-
-async def get_suggestions_with_eval(query: str, annotation: str, context: RunContext) -> list[dict]:
-    """获取推荐词并评估"""
-    print(f"\n正在获取推荐词:{query}")
-
-    # 1. 调用小红书API
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-    query_suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-    print(f"获取到 {len(query_suggestions) if query_suggestions else 0} 个推荐词:{query_suggestions}")
-
-    if not query_suggestions:
-        # 记录到历史
-        context.operations_history.append({
-            "operation_type": "get_query_suggestions",
-            "timestamp": datetime.now().isoformat(),
-            "query": query,
-            "suggestions": [],
-            "evaluations": "未返回任何推荐词",
-        })
-        return []
-
-    # 2. 并发评估所有推荐词
-    async def evaluate_single_query(q_sug: str):
-        eval_input = f"""
-<原始问题标注(三层)>
-{annotation}
-</原始问题标注(三层)>
-
-<待评估的推荐query>
-{q_sug}
-</待评估的推荐query>
-
-请评估该推荐query的三个分数:
-1. essence_score: 本质/意图是否一致(0或1)
-2. hard_score: 硬性约束是否满足(0或1)
-3. soft_score: 软性修饰保留程度(0-1)
-4. reason: 详细的评估理由
-"""
-        evaluator_result = await Runner.run(evaluator, eval_input)
-        result: EvaluationFeedback = evaluator_result.final_output
-        return {
-            "query": q_sug,
-            "essence_score": result.essence_score,
-            "hard_score": result.hard_score,
-            "soft_score": result.soft_score,
-            "reason": result.reason,
-        }
-
-    evaluations = await asyncio.gather(*[evaluate_single_query(q_sug) for q_sug in query_suggestions])
-
-    # 3. 记录到历史
-    context.operations_history.append({
-        "operation_type": "get_query_suggestions",
-        "timestamp": datetime.now().isoformat(),
-        "query": query,
-        "suggestions": query_suggestions,
-        "evaluations": evaluations,
-    })
-
-    return evaluations
-
-
-async def generate_modification_strategy(
-    current_query: str,
-    evaluations: list[dict],
-    annotation: str,
-    context: RunContext
-) -> ModificationStrategy:
-    """生成修改策略"""
-    print("\n正在生成修改策略...")
-
-    # 整理历史尝试记录 - 完整保留推荐词和评估结果
-    history_records = []
-    round_num = 0
-
-    for op in context.operations_history:
-        if op["operation_type"] == "get_query_suggestions":
-            round_num += 1
-            record = {
-                "round": round_num,
-                "query": op["query"],
-                "suggestions": op["suggestions"],
-                "evaluations": op["evaluations"]
-            }
-            history_records.append(record)
-        elif op["operation_type"] == "modify_query":
-            # 修改操作也记录,但不增加轮数
-            history_records.append({
-                "operation": "modify_query",
-                "strategy_type": op.get("strategy_type", op.get("modification_type")),  # 兼容旧字段
-                "base_query": op.get("base_query"),
-                "base_query_source": op.get("base_query_source"),
-                "modification_actions": op.get("modification_actions", []),
-                "original_query": op["original_query"],
-                "new_query": op["new_query"],
-                "reasoning": op["reasoning"]
-            })
-
-    # 格式化历史记录为JSON
-    history_json = json.dumps(history_records, ensure_ascii=False, indent=2)
-
-    strategy_input = f"""
-<原始问题标注(三层)>
-{annotation}
-</原始问题标注(三层)>
-
-<历史尝试记录(完整)>
-{history_json}
-</历史尝试记录(完整)>
-
-<当前query>
-{current_query}
-</当前query>
-
-<当前轮推荐词评估结果>
-{json.dumps(evaluations, ensure_ascii=False, indent=2) if evaluations else "空列表"}
-</当前轮推荐词评估结果>
-
-请基于所有历史尝试和当前评估结果,生成下一步的query修改策略。
-
-重点分析:
-
-1. **当前推荐词的信号**:
-   - 系统理解成什么主题了?(旅游?教程?素材?工具?品种?)
-   - 推荐词揭示了什么信息?系统在哪个方向有内容?
-   - **不要只看essence_score**:essence=0的推荐词也可能是好的探索起点
-   - 哪个推荐词最有潜力作为下一步的桥梁?
-
-2. **历史趋势分析**:
-   - 推荐词的主题变化:从"品种介绍"→"旅游"→"摄影"?
-   - 是否在逐步接近目标?还是原地打转(主题不变)?
-   - 哪些query让系统理解方向改变了?
-
-3. **确定探索目的**:
-   - 下一步query的探索目的是什么?
-     * 验证系统对某个词的理解?
-     * 往某个方向引导系统?
-     * 利用推荐词作为桥梁?
-   - **记住**:中间query不需要满足原需求,关键是达成探索目的
-"""
-    result = await Runner.run(strategy_generator, strategy_input)
-    strategy: ModificationStrategy = result.final_output
-    return strategy
-
-
-def find_qualified_queries(evaluations: list[dict], min_soft_score: float = 0.7) -> list[dict]:
-    """查找所有合格的query,按soft_score降序排列"""
-    qualified = [
-        e for e in evaluations
-        if e['essence_score'] == 1
-        and e['hard_score'] == 1
-        and e['soft_score'] >= min_soft_score
-    ]
-    # 按soft_score降序排列
-    return sorted(qualified, key=lambda x: x['soft_score'], reverse=True)
-
-
-# ============================================================================
-# 主流程(代码控制)
-# ============================================================================
-
-async def optimize_query(context: RunContext, max_rounds: int = 20) -> dict:
-    """
-    主优化流程 - 由代码控制
-
-    Args:
-        context: 运行上下文
-        max_rounds: 最大迭代轮数,默认20
-
-    返回格式:
-    {
-        "success": True/False,
-        "result": {...} or None,
-        "message": "..."
-    }
-    """
-    # 1. 标注问题(仅一次)
-    annotation = await annotate_question(context.q_with_context)
-    context.question_annotation = annotation
-
-    # 2. 迭代优化
-    current_query = context.q
-
-    for round_num in range(1, max_rounds + 1):
-        print(f"\n{'='*60}")
-        print(f"第 {round_num} 轮:{'使用原始问题' if round_num == 1 else '使用修改后的query'}")
-        print(f"当前query: {current_query}")
-        print(f"{'='*60}")
-
-        # 获取推荐词并评估
-        evaluations = await get_suggestions_with_eval(current_query, annotation, context)
-
-        if evaluations:
-            # 检查是否找到合格query
-            qualified_queries = find_qualified_queries(evaluations, min_soft_score=0.7)
-            if qualified_queries:
-                return {
-                    "success": True,
-                    "results": qualified_queries,
-                    "message": f"第{round_num}轮找到{len(qualified_queries)}个合格query"
-                }
-
-        # 如果是最后一轮,不再生成策略
-        if round_num == max_rounds:
-            break
-
-        # 生成修改策略
-        print(f"\n--- 生成修改策略 ---")
-        strategy = await generate_modification_strategy(current_query, evaluations, annotation, context)
-
-        print(f"\n修改策略:")
-        print(f"  推理过程:{strategy.reasoning}")
-        print(f"  策略类型:{strategy.strategy_type}")
-        print(f"  基础query:{strategy.base_query} (来源: {strategy.base_query_source})")
-        print(f"  修改动作:{', '.join(strategy.modification_actions)}")
-        print(f"  新query:{strategy.new_query}")
-
-        # 记录修改
-        context.operations_history.append({
-            "operation_type": "modify_query",
-            "timestamp": datetime.now().isoformat(),
-            "reasoning": strategy.reasoning,
-            "strategy_type": strategy.strategy_type,
-            "base_query": strategy.base_query,
-            "base_query_source": strategy.base_query_source,
-            "modification_actions": strategy.modification_actions,
-            "original_query": current_query,
-            "new_query": strategy.new_query,
-        })
-
-        # 更新当前query
-        current_query = strategy.new_query
-
-    # 所有轮次后仍未找到,降低标准查找
-    print(f"\n{'='*60}")
-    print(f"{max_rounds}轮后未找到最优query,降低标准(soft_score >= 0.5)")
-    print(f"{'='*60}")
-
-    acceptable_queries = find_qualified_queries(evaluations, min_soft_score=0.5)
-    if acceptable_queries:
-        return {
-            "success": True,
-            "results": acceptable_queries,
-            "message": f"{max_rounds}轮后找到{len(acceptable_queries)}个可接受query(soft_score >= 0.5)"
-        }
-
-    # 完全失败:找出最接近的
-    essence_hard_ok = [
-        e for e in evaluations
-        if e['essence_score'] == 1 and e['hard_score'] == 1
-    ]
-    if essence_hard_ok:
-        # 返回所有满足essence和hard的,按soft_score降序
-        closest_queries = sorted(essence_hard_ok, key=lambda x: x['soft_score'], reverse=True)
-        return {
-            "success": False,
-            "results": closest_queries,
-            "message": f"未找到合格query,但有{len(closest_queries)}个接近的推荐词(essence=1, hard=1)"
-        }
-
-    return {
-        "success": False,
-        "results": [],
-        "message": "未找到任何满足本质和硬性约束的推荐词"
-    }
-
-
-# ============================================================================
-# 输出格式化
-# ============================================================================
-
-def format_output(optimization_result: dict, context: RunContext) -> str:
-    """格式化输出结果"""
-    results = optimization_result.get("results", [])
-
-    if optimization_result["success"] and results:
-        output = f"原始问题:{context.q}\n"
-        output += f"状态:{optimization_result['message']}\n\n"
-        output += "合格的推荐query(按soft_score降序):\n"
-        for i, result in enumerate(results, 1):
-            output += f"\n{i}. {result['query']}\n"
-            output += f"   - 本质匹配度:{result['essence_score']} (1=本质一致)\n"
-            output += f"   - 硬性约束匹配度:{result['hard_score']} (1=所有约束满足)\n"
-            output += f"   - 软性修饰完整度:{result['soft_score']:.2f} (0-1)\n"
-            output += f"   - 评估理由:{result['reason']}\n"
-        return output.strip()
-    else:
-        output = f"原始问题:{context.q}\n"
-        output += f"结果:未找到合格推荐query\n"
-        output += f"原因:{optimization_result['message']}\n"
-
-        if results:
-            output += "\n最接近的推荐词(按soft_score降序):\n"
-            for i, result in enumerate(results[:3], 1):  # 只显示前3个
-                output += f"\n{i}. {result['query']}\n"
-                output += f"   - essence_score: {result['essence_score']}\n"
-                output += f"   - hard_score: {result['hard_score']}\n"
-                output += f"   - soft_score: {result['soft_score']:.2f}\n"
-                output += f"   - reason: {result['reason']}\n"
-
-        output += "\n建议:尝试简化问题或调整需求描述"
-        return output.strip()
-
-
-# ============================================================================
-# 主函数
-# ============================================================================
-
-async def main(input_dir: str, max_rounds: int = 20):
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]
-
-    # 日志保存目录
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-
-    # 执行优化流程(代码控制)
-    optimization_result = await optimize_query(run_context, max_rounds=max_rounds)
-
-    # 格式化输出
-    final_output = format_output(optimization_result, run_context)
-    print(f"\n{'='*60}")
-    print("最终结果")
-    print(f"{'='*60}")
-    print(final_output)
-
-    # 保存结果
-    run_context.final_output = final_output
-
-    # 保存 RunContext 到 log_dir
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    parser.add_argument(
-        "--max-rounds",
-        type=int,
-        default=20,
-        help="最大迭代轮数,默认: 20"
-    )
-    args = parser.parse_args()
-
-    asyncio.run(main(args.input_dir, max_rounds=args.max_rounds))

+ 0 - 728
sug_v5_3.py

@@ -1,728 +0,0 @@
-import asyncio
-import json
-import os
-import argparse
-from datetime import datetime
-
-from agents import Agent, Runner
-from lib.my_trace import set_trace
-from typing import Literal
-from pydantic import BaseModel, Field
-
-from lib.utils import read_file_as_string
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-    question_annotation: str | None = Field(default=None, description="问题的标注结果")
-    operations_history: list[dict] = Field(default_factory=list, description="记录所有操作的历史")
-    optimization_result: dict | None = Field(default=None, description="最终优化结果对象")
-    final_output: str | None = Field(default=None, description="最终输出结果(格式化文本)")
-
-
-# ============================================================================
-# Agent 1: 问题标注专家
-# ============================================================================
-question_annotation_instructions = """
-你是搜索需求分析专家。给定问题(含需求背景),在原文上标注三层:本质、硬性、软性。
-
-## 判断标准
-
-**[本质]** - 问题的核心意图
-- 如何获取、教程、推荐、作品、测评等
-
-**[硬]** - 客观事实性约束(可明确验证、非主观判断)
-- 能明确区分类别的:地域、时间、对象、工具、操作类型
-- 特征:改变后得到完全不同类别的结果
-
-**[软]** - 主观判断性修饰(因人而异、程度性的)
-- 需要主观评价的:质量、速度、美观、特色、程度
-- 特征:改变后仍是同类结果,只是满足程度不同
-
-## 输出格式
-
-词语[本质-描述]、词语[硬-描述]、词语[软-描述]
-
-## 注意
-- 只输出标注后的字符串
-- 结合需求背景判断意图
-""".strip()
-
-question_annotator = Agent[None](
-    name="问题标注专家",
-    instructions=question_annotation_instructions,
-)
-
-
-# ============================================================================
-# Agent 2: 评估专家
-# ============================================================================
-eval_instructions = """
-你是搜索query评估专家。给定原始问题标注(三层)和推荐query,评估三个分数。
-
-## 评估目标
-
-用这个推荐query搜索,能否找到满足原始需求的内容?
-
-## 三层评分
-
-### 1. essence_score(本质/意图)= 0 或 1
-
-推荐query的本质/意图是否与原问题一致?
-
-**原问题标注中的[本质-XXX]对应推荐词要求:**
-- 找方法/如何获取 → 推荐词**必须明确包含方法/途径类词汇**
-  - ✅ "...网站推荐"、"如何获取..."、"...获取途径"、"...方法"
-  - ❌ "...下载"、"...素材"(直接找内容,不是找方法)
-- 教程/学习 → 推荐词应该是教程/教学
-- 作品/欣赏 → 推荐词应该是作品展示
-- 工具/推荐 → 推荐词应该是工具推荐
-
-**评分原则:**
-- 1 = 本质一致,推荐词**明确表达**相同意图
-- 0 = 本质改变或**不够明确**(宁可严格,不可放松)
-
-### 2. hard_score(硬性约束)= 0 或 1
-
-在本质一致的前提下,是否满足所有硬性约束?
-
-**原问题标注中的[硬-XXX]:**地域、时间、对象、质量、工具等
-
-**评分:**
-- 1 = 所有硬性约束都满足
-- 0 = 任一硬性约束不满足
-
-### 3. soft_score(软性修饰)= 0-1
-
-软性修饰词保留了多少?
-
-**评分参考:**
-- 1.0 = 完整保留
-- 0.7-0.9 = 保留核心
-- 0.4-0.6 = 部分丢失
-- 0-0.3 = 大量丢失
-
-## 注意
-
-- essence=0 直接拒绝,不管hard/soft多高
-- essence=1, hard=0 也要拒绝
-- essence=1, hard=1 才看soft_score
-""".strip()
-
-class EvaluationFeedback(BaseModel):
-    """评估反馈模型 - 三层评分"""
-    essence_score: Literal[0, 1] = Field(..., description="本质/意图匹配度,0或1")
-    hard_score: Literal[0, 1] = Field(..., description="硬性约束匹配度,0或1")
-    soft_score: float = Field(..., description="软性修饰完整度,0-1")
-    reason: str = Field(..., description="评估理由")
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_instructions,
-    output_type=EvaluationFeedback,
-)
-
-
-# ============================================================================
-# Agent 3: 修改策略生成专家
-# ============================================================================
-strategy_instructions = """
-你是query修改策略专家。**模拟人在搜索引擎中的真实搜索行为**,基于反馈动态调整query。
-
-## 核心思路:搜索是探索过程,不是直达过程
-
-**关键认知:**
-1. **中间query不需要满足原始需求** - 它是探索工具,可以偏离原需求
-2. **推荐词是最宝贵的反馈信号** - 告诉你系统理解成什么了,有什么内容
-3. **每一步query都有明确的探索目的** - 不是盲目改词,而是试探和引导
-4. **最终目标:找到满足需求的推荐词** - 不是让query本身满足需求
-
-## 人的真实搜索过程
-
-**搜索的本质**:通过多步探索,利用推荐词作为桥梁,逐步引导系统
-
-**典型模式**:
-
-第1步:直接尝试
-- 目的:看系统能否直接理解
-- 结果:空列表或essence=0
-- essence=0的推荐词:告诉你系统理解成什么了
-
-第2步:降低要求,简化query
-- 目的:让系统有响应,看它在基础层面有什么
-- 推荐词虽然essence=0,但揭示了系统在某个主题有内容
-- **关键**:选一个最有潜力的推荐词
-
-第3步:基于推荐词,往目标方向引导
-- 目的:利用推荐词作为桥梁,加上目标方向的词
-- 推荐词还是essence=0,但主题在变化(接近目标)
-- **渐进式**:不求一步到位,每步都有进展
-
-第4步:继续引导或换角度
-- 如果推荐词主题不变 → 换角度
-- 如果推荐词主题在接近 → 继续引导
-
-最终:找到essence=1的推荐词
-
-**关键原则**:
-1. essence_score是评估推荐词的,不是评估中间query的
-2. essence=0的推荐词也有价值,它揭示了系统的理解方向
-3. 每一步都有明确的探索目的,看目的是否达成
-4. 通过推荐词的主题变化,判断是否在接近目标
-
-## 输入信息
-- 原始问题标注(三层):本质、硬性约束、软性修饰
-- 历史尝试记录:所有轮次的query、推荐词、评估结果
-- 当前query和推荐词评估
-
-## 分析步骤
-
-### 第一步:理解当前推荐词的信号
-**核心问题:推荐词告诉我什么信息?**
-
-**重要提醒:essence_score是评估推荐词是否满足原始需求的最终目标**
-- essence_score=1: 推荐词满足原需求的本质
-- essence_score=0: 推荐词不满足原需求的本质
-- **但中间query的目的可能不是满足原需求**,所以essence_score只是参考
-
-1. **系统理解层面**(看推荐词的主题):
-   - 空列表 → 系统完全不理解当前query
-   - 有推荐词 → 系统理解成了什么主题?
-     - 旅游?教程?素材?工具?品种介绍?
-     - 这些主题是否有助于往目标方向引导?
-
-2. **内容可用性层面**(看推荐词的价值):
-   - **即使推荐词essence=0,也可能是很好的探索起点**
-   - 例如:推荐词"川西旅游攻略"虽然essence=0,但揭示了系统认识"川西"
-   - 哪些推荐词最有潜力作为下一步的桥梁?
-
-3. **探索目的验证**:
-   - 当前query的探索目的是什么?达到了吗?
-   - 例如:目的是"看系统对川西有什么" → 达到了(有推荐词)
-   - 下一步要验证/探索什么?
-
-### 第二步:回顾历史,识别规律
-- 哪些query让系统理解方向变化了?(从"旅游"变成"摄影")
-- 哪些方向是死路?(多次essence=0且推荐词主题不变)
-- **是否有渐进的改善?**(推荐词越来越接近目标)
-
-### 第三步:选择策略类型(带着明确的探索目的)
-
-**refine_current(微调当前query)**
-- 适用:推荐词方向对了,需要微调让它更精确
-- 探索目的:在正确方向上精细化
-- 动作:加词/减词/换词/调整顺序
-
-**use_recommendation(选推荐词作为新起点)** ⭐ 最重要策略
-- 适用:推荐词虽然essence=0,但**揭示了系统在这个方向有内容**
-- 探索目的:利用推荐词这个客观信号,引导系统往目标方向
-- **核心思维**:推荐词是系统给你的提示,告诉你"我有这个"
-- 动作:
-  - 选一个最有潜力的推荐词作为base_query
-  - 在它基础上加目标方向的词
-  - **这个新query可能不满足原需求,但目的是探索和引导**
-
-**change_approach(换完全不同的角度)**
-- 适用:当前方向是死路(多次尝试推荐词主题不变)
-- 探索目的:跳出当前框架,从另一个角度切入
-- 动作:换一种完全不同的表述方式
-
-**relax_constraints(放宽约束)**
-- 适用:query太复杂,系统不理解(返回空列表)
-- 探索目的:先让系统有响应,看它在最基础层面有什么
-- 动作:去掉限定词,保留核心概念
-
-## 输出要求
-
-### 1. reasoning(推理过程)
-必须包含三部分,**重点写探索目的**:
-
-- **当前推荐词信号分析**:
-  - 系统理解成什么主题了?(旅游?教程?素材?工具?品种?)
-  - 推荐词揭示了什么信息?(系统在哪个方向有内容)
-  - **不要只看essence_score**:
-    - essence_score=0不代表推荐词没用
-    - 关键看推荐词的主题是否有助于引导
-  - 哪个推荐词最有潜力作为下一步的桥梁?
-
-- **历史尝试与趋势**:
-  - 系统理解的主题变化:从"品种介绍"→"旅游"→"摄影"
-  - 是否在逐步接近目标?还是原地打转?
-
-- **下一步策略与探索目的**:
-  - **这一步query的探索目的是什么?**
-    - 验证系统对某个词的理解?
-    - 往某个方向引导?
-    - 利用推荐词作为桥梁?
-  - 为什么选这个base_query?
-  - 为什么这样修改?
-  - **重要**:不要纠结"这个query不满足原需求",关键是它能否达成探索目的
-
-### 2. strategy_type
-从4种策略中选择:refine_current, use_recommendation, change_approach, relax_constraints
-
-### 3. base_query
-**关键**:可以选择历史中的query,也可以选择历史推荐词
-- 如果选历史query:base_query_source = "history_query"
-- 如果选历史推荐词:base_query_source = "history_recommendation"
-
-### 4. base_query_source
-说明base_query的来源
-
-### 5. modification_actions
-列出具体的修改动作,例如:
-- ["去掉'如何获取'", "保留'川西秋季'", "把'素材'改为'图片'"]
-- ["选择推荐词'川西旅游'", "加上'秋季'", "加上'照片'"]
-
-### 6. new_query
-最终的新query
-
-## 重要原则
-
-1. **推荐词是最宝贵的反馈** - 充分利用推荐词这个客观信号
-   - 即使essence=0的推荐词,也揭示了系统在这个方向有什么
-   - **优先考虑use_recommendation策略** - 选一个推荐词作为起点
-
-2. **中间query可以偏离原需求** - 每一步都有明确的探索目的
-   - 不要纠结"这个query不满足原需求"
-   - 关键是:这个query能不能帮你往正确方向引导系统
-
-3. **识别死胡同,及时换方向**
-   - 如果多次尝试推荐词主题不变 → 换方向
-   - 如果推荐词越来越偏 → 回退到之前的某个好的起点
-
-4. **保持推理简洁** - 抓住关键信息
-   - 明确说出探索目的
-   - 不要重复啰嗦
-""".strip()
-
-class ModificationStrategy(BaseModel):
-    """修改策略模型 - 模拟人的搜索调整过程"""
-    reasoning: str = Field(..., description="推理过程:1)当前推荐词分析:系统理解成什么了?2)历史尝试总结:哪些方向有效/无效?3)下一步策略:为什么这样调整?")
-
-    strategy_type: Literal[
-        "refine_current",      # 微调当前query(加词/减词/换词/换顺序)
-        "use_recommendation",  # 选择推荐词作为新起点,在它基础上修改
-        "change_approach",     # 换完全不同的表述角度
-        "relax_constraints"    # 放宽约束,去掉部分限定词
-    ] = Field(..., description="策略类型")
-
-    base_query: str = Field(..., description="基础query,可以是:1)历史中的query 2)历史推荐词中的某一个")
-    base_query_source: Literal["history_query", "history_recommendation"] = Field(..., description="base_query的来源")
-
-    modification_actions: list[str] = Field(..., description="具体修改动作的描述,如:['去掉\"如何获取\"', '保留核心词\"川西秋季\"', '把\"素材\"改为\"图片\"']")
-
-    new_query: str = Field(..., description="修改后的新query")
-
-strategy_generator = Agent[None](
-    name="策略生成专家",
-    instructions=strategy_instructions,
-    output_type=ModificationStrategy,
-)
-
-
-# ============================================================================
-# 核心函数
-# ============================================================================
-
-async def annotate_question(q_with_context: str) -> str:
-    """标注问题(三层)"""
-    print("\n正在标注问题...")
-    result = await Runner.run(question_annotator, q_with_context)
-    annotation = str(result.final_output)
-    print(f"问题标注完成:{annotation}")
-    return annotation
-
-
-async def get_suggestions_with_eval(query: str, annotation: str, context: RunContext) -> list[dict]:
-    """获取推荐词并评估"""
-    print(f"\n正在获取推荐词:{query}")
-
-    # 1. 调用小红书API
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-    query_suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-    print(f"获取到 {len(query_suggestions) if query_suggestions else 0} 个推荐词:{query_suggestions}")
-
-    if not query_suggestions:
-        # 记录到历史
-        context.operations_history.append({
-            "operation_type": "get_query_suggestions",
-            "timestamp": datetime.now().isoformat(),
-            "query": query,
-            "suggestions": [],
-            "evaluations": "未返回任何推荐词",
-        })
-        return []
-
-    # 2. 并发评估所有推荐词
-    async def evaluate_single_query(q_sug: str):
-        eval_input = f"""
-<原始问题标注(三层)>
-{annotation}
-</原始问题标注(三层)>
-
-<待评估的推荐query>
-{q_sug}
-</待评估的推荐query>
-
-请评估该推荐query的三个分数:
-1. essence_score: 本质/意图是否一致(0或1)
-2. hard_score: 硬性约束是否满足(0或1)
-3. soft_score: 软性修饰保留程度(0-1)
-4. reason: 详细的评估理由
-"""
-        evaluator_result = await Runner.run(evaluator, eval_input)
-        result: EvaluationFeedback = evaluator_result.final_output
-        return {
-            "query": q_sug,
-            "essence_score": result.essence_score,
-            "hard_score": result.hard_score,
-            "soft_score": result.soft_score,
-            "reason": result.reason,
-        }
-
-    evaluations = await asyncio.gather(*[evaluate_single_query(q_sug) for q_sug in query_suggestions])
-
-    # 3. 记录到历史
-    context.operations_history.append({
-        "operation_type": "get_query_suggestions",
-        "timestamp": datetime.now().isoformat(),
-        "query": query,
-        "suggestions": query_suggestions,
-        "evaluations": evaluations,
-    })
-
-    return evaluations
-
-
-async def generate_modification_strategy(
-    current_query: str,
-    evaluations: list[dict],
-    annotation: str,
-    context: RunContext
-) -> ModificationStrategy:
-    """生成修改策略"""
-    print("\n正在生成修改策略...")
-
-    # 整理历史尝试记录 - 完整保留推荐词和评估结果
-    history_records = []
-    round_num = 0
-
-    for op in context.operations_history:
-        if op["operation_type"] == "get_query_suggestions":
-            round_num += 1
-            record = {
-                "round": round_num,
-                "query": op["query"],
-                "suggestions": op["suggestions"],
-                "evaluations": op["evaluations"]
-            }
-            history_records.append(record)
-        elif op["operation_type"] == "modify_query":
-            # 修改操作也记录,但不增加轮数
-            history_records.append({
-                "operation": "modify_query",
-                "strategy_type": op.get("strategy_type", op.get("modification_type")),  # 兼容旧字段
-                "base_query": op.get("base_query"),
-                "base_query_source": op.get("base_query_source"),
-                "modification_actions": op.get("modification_actions", []),
-                "original_query": op["original_query"],
-                "new_query": op["new_query"],
-                "reasoning": op["reasoning"]
-            })
-
-    # 格式化历史记录为JSON
-    history_json = json.dumps(history_records, ensure_ascii=False, indent=2)
-
-    strategy_input = f"""
-<原始问题标注(三层)>
-{annotation}
-</原始问题标注(三层)>
-
-<历史尝试记录(完整)>
-{history_json}
-</历史尝试记录(完整)>
-
-<当前query>
-{current_query}
-</当前query>
-
-<当前轮推荐词评估结果>
-{json.dumps(evaluations, ensure_ascii=False, indent=2) if evaluations else "空列表"}
-</当前轮推荐词评估结果>
-
-请基于所有历史尝试和当前评估结果,生成下一步的query修改策略。
-
-重点分析:
-
-1. **当前推荐词的信号**:
-   - 系统理解成什么主题了?(旅游?教程?素材?工具?品种?)
-   - 推荐词揭示了什么信息?系统在哪个方向有内容?
-   - **不要只看essence_score**:essence=0的推荐词也可能是好的探索起点
-   - 哪个推荐词最有潜力作为下一步的桥梁?
-
-2. **历史趋势分析**:
-   - 推荐词的主题变化:从"品种介绍"→"旅游"→"摄影"?
-   - 是否在逐步接近目标?还是原地打转(主题不变)?
-   - 哪些query让系统理解方向改变了?
-
-3. **确定探索目的**:
-   - 下一步query的探索目的是什么?
-     * 验证系统对某个词的理解?
-     * 往某个方向引导系统?
-     * 利用推荐词作为桥梁?
-   - **记住**:中间query不需要满足原需求,关键是达成探索目的
-"""
-    result = await Runner.run(strategy_generator, strategy_input)
-    strategy: ModificationStrategy = result.final_output
-    return strategy
-
-
-def find_qualified_queries(evaluations: list[dict], min_soft_score: float = 0.7) -> list[dict]:
-    """查找所有合格的query,按soft_score降序排列"""
-    qualified = [
-        e for e in evaluations
-        if e['essence_score'] == 1
-        and e['hard_score'] == 1
-        and e['soft_score'] >= min_soft_score
-    ]
-    # 按soft_score降序排列
-    return sorted(qualified, key=lambda x: x['soft_score'], reverse=True)
-
-
-# ============================================================================
-# 主流程(代码控制)
-# ============================================================================
-
-async def optimize_query(context: RunContext, max_rounds: int = 20) -> dict:
-    """
-    主优化流程 - 由代码控制
-
-    Args:
-        context: 运行上下文
-        max_rounds: 最大迭代轮数,默认20
-
-    返回格式:
-    {
-        "success": True/False,
-        "result": {...} or None,
-        "message": "..."
-    }
-    """
-    # 1. 标注问题(仅一次)
-    annotation = await annotate_question(context.q_with_context)
-    context.question_annotation = annotation
-
-    # 2. 迭代优化
-    current_query = context.q
-
-    for round_num in range(1, max_rounds + 1):
-        print(f"\n{'='*60}")
-        print(f"第 {round_num} 轮:{'使用原始问题' if round_num == 1 else '使用修改后的query'}")
-        print(f"当前query: {current_query}")
-        print(f"{'='*60}")
-
-        # 获取推荐词并评估
-        evaluations = await get_suggestions_with_eval(current_query, annotation, context)
-
-        if evaluations:
-            # 检查是否找到合格query
-            qualified_queries = find_qualified_queries(evaluations, min_soft_score=0.7)
-            if qualified_queries:
-                return {
-                    "success": True,
-                    "results": qualified_queries,
-                    "message": f"第{round_num}轮找到{len(qualified_queries)}个合格query"
-                }
-
-        # 如果是最后一轮,不再生成策略
-        if round_num == max_rounds:
-            break
-
-        # 生成修改策略
-        print(f"\n--- 生成修改策略 ---")
-        strategy = await generate_modification_strategy(current_query, evaluations, annotation, context)
-
-        print(f"\n修改策略:")
-        print(f"  推理过程:{strategy.reasoning}")
-        print(f"  策略类型:{strategy.strategy_type}")
-        print(f"  基础query:{strategy.base_query} (来源: {strategy.base_query_source})")
-        print(f"  修改动作:{', '.join(strategy.modification_actions)}")
-        print(f"  新query:{strategy.new_query}")
-
-        # 记录修改
-        context.operations_history.append({
-            "operation_type": "modify_query",
-            "timestamp": datetime.now().isoformat(),
-            "reasoning": strategy.reasoning,
-            "strategy_type": strategy.strategy_type,
-            "base_query": strategy.base_query,
-            "base_query_source": strategy.base_query_source,
-            "modification_actions": strategy.modification_actions,
-            "original_query": current_query,
-            "new_query": strategy.new_query,
-        })
-
-        # 更新当前query
-        current_query = strategy.new_query
-
-    # 所有轮次后仍未找到,降低标准查找
-    print(f"\n{'='*60}")
-    print(f"{max_rounds}轮后未找到最优query,降低标准(soft_score >= 0.5)")
-    print(f"{'='*60}")
-
-    acceptable_queries = find_qualified_queries(evaluations, min_soft_score=0.5)
-    if acceptable_queries:
-        return {
-            "success": True,
-            "results": acceptable_queries,
-            "message": f"{max_rounds}轮后找到{len(acceptable_queries)}个可接受query(soft_score >= 0.5)"
-        }
-
-    # 完全失败:找出最接近的
-    essence_hard_ok = [
-        e for e in evaluations
-        if e['essence_score'] == 1 and e['hard_score'] == 1
-    ]
-    if essence_hard_ok:
-        # 返回所有满足essence和hard的,按soft_score降序
-        closest_queries = sorted(essence_hard_ok, key=lambda x: x['soft_score'], reverse=True)
-        return {
-            "success": False,
-            "results": closest_queries,
-            "message": f"未找到合格query,但有{len(closest_queries)}个接近的推荐词(essence=1, hard=1)"
-        }
-
-    return {
-        "success": False,
-        "results": [],
-        "message": "未找到任何满足本质和硬性约束的推荐词"
-    }
-
-
-# ============================================================================
-# 输出格式化
-# ============================================================================
-
-def format_output(optimization_result: dict, context: RunContext) -> str:
-    """格式化输出结果"""
-    results = optimization_result.get("results", [])
-
-    if optimization_result["success"] and results:
-        output = f"原始问题:{context.q}\n"
-        output += f"状态:{optimization_result['message']}\n\n"
-        output += "合格的推荐query(按soft_score降序):\n"
-        for i, result in enumerate(results, 1):
-            output += f"\n{i}. {result['query']}\n"
-            output += f"   - 本质匹配度:{result['essence_score']} (1=本质一致)\n"
-            output += f"   - 硬性约束匹配度:{result['hard_score']} (1=所有约束满足)\n"
-            output += f"   - 软性修饰完整度:{result['soft_score']:.2f} (0-1)\n"
-            output += f"   - 评估理由:{result['reason']}\n"
-        return output.strip()
-    else:
-        output = f"原始问题:{context.q}\n"
-        output += f"结果:未找到合格推荐query\n"
-        output += f"原因:{optimization_result['message']}\n"
-
-        if results:
-            output += "\n最接近的推荐词(按soft_score降序):\n"
-            for i, result in enumerate(results[:3], 1):  # 只显示前3个
-                output += f"\n{i}. {result['query']}\n"
-                output += f"   - essence_score: {result['essence_score']}\n"
-                output += f"   - hard_score: {result['hard_score']}\n"
-                output += f"   - soft_score: {result['soft_score']:.2f}\n"
-                output += f"   - reason: {result['reason']}\n"
-
-        output += "\n建议:尝试简化问题或调整需求描述"
-        return output.strip()
-
-
-# ============================================================================
-# 主函数
-# ============================================================================
-
-async def main(input_dir: str, max_rounds: int = 20):
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]
-
-    # 日志保存目录
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-
-    # 执行优化流程(代码控制)
-    optimization_result = await optimize_query(run_context, max_rounds=max_rounds)
-
-    # 格式化输出
-    final_output = format_output(optimization_result, run_context)
-    print(f"\n{'='*60}")
-    print("最终结果")
-    print(f"{'='*60}")
-    print(final_output)
-
-    # 保存结果
-    run_context.optimization_result = optimization_result
-    run_context.final_output = final_output
-
-    # 保存 RunContext 到 log_dir
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    parser.add_argument(
-        "--max-rounds",
-        type=int,
-        default=20,
-        help="最大迭代轮数,默认: 20"
-    )
-    args = parser.parse_args()
-
-    asyncio.run(main(args.input_dir, max_rounds=args.max_rounds))

+ 0 - 632
sug_v6_0_progressive_exploration.py

@@ -1,632 +0,0 @@
-import asyncio
-import json
-import os
-import argparse
-from datetime import datetime
-
-from agents import Agent, Runner
-from lib.my_trace import set_trace
-from typing import Literal
-from pydantic import BaseModel, Field
-
-from lib.utils import read_file_as_string
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-
-    # 探索阶段记录
-    keywords: list[str] | None = Field(default=None, description="提取的关键词")
-    exploration_levels: list[dict] = Field(default_factory=list, description="每一层的探索结果")
-    level_analyses: list[dict] = Field(default_factory=list, description="每一层的主Agent分析")
-
-    # 最终结果
-    final_candidates: list[str] | None = Field(default=None, description="最终选出的候选query")
-    evaluation_results: list[dict] | None = Field(default=None, description="候选query的评估结果")
-    optimization_result: dict | None = Field(default=None, description="最终优化结果对象")
-    final_output: str | None = Field(default=None, description="最终输出结果(格式化文本)")
-
-
-# ============================================================================
-# Agent 1: 关键词提取专家
-# ============================================================================
-keyword_extraction_instructions = """
-你是关键词提取专家。给定一个搜索问题(含上下文),提取出**最细粒度的关键概念**。
-
-## 提取原则
-
-1. **细粒度优先**:拆分成最小的有意义单元
-   - 不要保留完整的长句
-   - 拆分成独立的、有搜索意义的词或短语
-
-2. **保留核心维度**:
-   - 地域/对象
-   - 时间
-   - 行为/意图:获取、教程、推荐、如何等
-   - 主题/领域
-   - 质量/属性
-
-3. **去掉无意义的虚词**:的、吗、呢等
-
-4. **保留领域专有词**:不要过度拆分专业术语
-   - 如果是常见的组合词,保持完整
-
-## 输出要求
-
-输出关键词列表,按重要性排序(最核心的在前)。
-""".strip()
-
-class KeywordList(BaseModel):
-    """关键词列表"""
-    keywords: list[str] = Field(..., description="提取的关键词,按重要性排序")
-    reasoning: str = Field(..., description="提取理由")
-
-keyword_extractor = Agent[None](
-    name="关键词提取专家",
-    instructions=keyword_extraction_instructions,
-    output_type=KeywordList,
-)
-
-
-# ============================================================================
-# Agent 2: 层级探索分析专家
-# ============================================================================
-level_analysis_instructions = """
-你是搜索空间探索分析专家。基于当前层级的探索结果,决定下一步行动。
-
-## 你的任务
-
-分析当前已探索的词汇空间,判断:
-1. **发现了什么有价值的信号?**
-2. **是否已经可以评估候选了?**
-3. **如果还不够,下一层应该探索什么组合?**
-
-## 分析维度
-
-### 1. 信号识别(最重要)
-
-看推荐词里**出现了什么主题**:
-
-**关键问题:**
-- 哪些推荐词**最接近原始需求**?
-- 哪些推荐词**揭示了有价值的方向**(即使不完全匹配)?
-- 哪些推荐词可以作为**下一层探索的桥梁**?
-- 系统对哪些概念理解得好?哪些理解偏了?
-
-### 2. 组合策略
-
-基于发现的信号,设计下一层探索:
-
-**组合类型:**
-
-a) **关键词直接组合**
-   - 两个关键词组合成新query
-
-b) **利用推荐词作为桥梁**(重要!)
-   - 发现某个推荐词很有价值 → 直接探索这个推荐词
-   - 或在推荐词基础上加其他关键词
-
-c) **跨层级组合**
-   - 结合多层发现的有价值推荐词
-   - 组合成更复杂的query
-
-### 3. 停止条件
-
-**何时可以评估候选?**
-
-满足以下之一:
-- 推荐词中出现了**明确包含原始需求多个核心要素的query**
-- 已经探索到**足够复杂的组合**(3-4个关键词),且推荐词相关
-- 探索了**3-4层**,信息已经足够丰富
-
-**何时继续探索?**
-- 当前推荐词太泛,没有接近原始需求
-- 发现了有价值的信号,但需要进一步组合验证
-- 层数还少(< 3层)
-
-## 输出要求
-
-### 1. key_findings
-总结当前层发现的关键信息,包括:
-- 哪些推荐词最有价值?
-- 系统对哪些概念理解得好/不好?
-- 发现了什么意外的方向?
-
-### 2. promising_signals
-列出最有价值的推荐词(来自任何已探索的query),每个说明为什么有价值
-
-### 3. should_evaluate_now
-是否已经可以开始评估候选了?true/false
-
-### 4. candidates_to_evaluate
-如果should_evaluate_now=true,列出应该评估的候选query
-- 可以是推荐词
-- 可以是自己构造的组合
-
-### 5. next_combinations
-如果should_evaluate_now=false,列出下一层应该探索的query组合
-
-### 6. reasoning
-详细的推理过程
-
-## 重要原则
-
-1. **不要过早评估**:至少探索2层,除非第一层就发现了完美匹配
-2. **充分利用推荐词**:推荐词是系统给的提示,要善用
-3. **保持探索方向的多样性**:不要只盯着一个方向
-4. **识别死胡同**:如果某个方向的推荐词一直不相关,果断放弃
-""".strip()
-
-class PromisingSignal(BaseModel):
-    """有价值的推荐词信号"""
-    query: str = Field(..., description="推荐词")
-    from_level: int = Field(..., description="来自哪一层")
-    reason: str = Field(..., description="为什么有价值")
-
-class LevelAnalysis(BaseModel):
-    """层级分析结果"""
-    key_findings: str = Field(..., description="当前层的关键发现")
-    promising_signals: list[PromisingSignal] = Field(..., description="有价值的推荐词信号")
-    should_evaluate_now: bool = Field(..., description="是否应该开始评估候选")
-    candidates_to_evaluate: list[str] = Field(default_factory=list, description="如果should_evaluate_now=true,要评估的候选query列表")
-    next_combinations: list[str] = Field(default_factory=list, description="如果should_evaluate_now=false,下一层要探索的query组合")
-    reasoning: str = Field(..., description="详细的推理过程")
-
-level_analyzer = Agent[None](
-    name="层级探索分析专家",
-    instructions=level_analysis_instructions,
-    output_type=LevelAnalysis,
-)
-
-
-# ============================================================================
-# Agent 3: 评估专家(复用v5_3的评估逻辑)
-# ============================================================================
-eval_instructions = """
-你是搜索query评估专家。给定原始问题和推荐query,评估三个分数。
-
-## 评估目标
-
-用这个推荐query搜索,能否找到满足原始需求的内容?
-
-## 三层评分
-
-### 1. essence_score(本质/意图)= 0 或 1
-
-推荐query的本质/意图是否与原问题一致?
-
-**判断标准:**
-- 原问题的核心意图是什么?(找方法、找教程、找作品、找工具、找资源等)
-- 推荐词是否明确表达了相同的意图?
-
-**评分原则:**
-- 1 = 本质一致,推荐词**明确表达**相同意图
-- 0 = 本质改变或**不够明确**
-
-### 2. hard_score(硬性约束)= 0 或 1
-
-在本质一致的前提下,是否满足所有硬性约束?
-
-**硬性约束**:地域、时间、对象、工具等客观可验证的限定
-
-**评分:**
-- 1 = 所有硬性约束都满足
-- 0 = 任一硬性约束不满足
-
-### 3. soft_score(软性修饰)= 0-1
-
-软性修饰词(质量、特色、美观等主观评价)保留了多少?
-
-**评分参考:**
-- 1.0 = 完整保留
-- 0.7-0.9 = 保留核心
-- 0.4-0.6 = 部分丢失
-- 0-0.3 = 大量丢失
-
-## 注意
-
-- essence=0 直接拒绝,不管hard/soft多高
-- essence=1, hard=0 也要拒绝
-- essence=1, hard=1 才看soft_score
-""".strip()
-
-class EvaluationFeedback(BaseModel):
-    """评估反馈模型 - 三层评分"""
-    essence_score: Literal[0, 1] = Field(..., description="本质/意图匹配度,0或1")
-    hard_score: Literal[0, 1] = Field(..., description="硬性约束匹配度,0或1")
-    soft_score: float = Field(..., description="软性修饰完整度,0-1")
-    reason: str = Field(..., description="评估理由")
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_instructions,
-    output_type=EvaluationFeedback,
-)
-
-
-# ============================================================================
-# 核心函数
-# ============================================================================
-
-async def extract_keywords(q: str) -> KeywordList:
-    """提取关键词"""
-    print("\n正在提取关键词...")
-    result = await Runner.run(keyword_extractor, q)
-    keyword_list: KeywordList = result.final_output
-    print(f"提取的关键词:{keyword_list.keywords}")
-    print(f"提取理由:{keyword_list.reasoning}")
-    return keyword_list
-
-
-async def explore_level(queries: list[str], level_num: int, context: RunContext) -> dict:
-    """探索一个层级(并发获取所有query的推荐词)"""
-    print(f"\n{'='*60}")
-    print(f"Level {level_num} 探索:{len(queries)} 个query")
-    print(f"{'='*60}")
-
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-
-    # 并发获取所有推荐词
-    async def get_single_sug(query: str):
-        print(f"  探索: {query}")
-        suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-        print(f"    → {len(suggestions) if suggestions else 0} 个推荐词")
-        return {
-            "query": query,
-            "suggestions": suggestions or []
-        }
-
-    results = await asyncio.gather(*[get_single_sug(q) for q in queries])
-
-    level_data = {
-        "level": level_num,
-        "timestamp": datetime.now().isoformat(),
-        "queries": results
-    }
-
-    context.exploration_levels.append(level_data)
-    return level_data
-
-
-async def analyze_level(level_data: dict, all_levels: list[dict], original_question: str, context: RunContext) -> LevelAnalysis:
-    """分析当前层级,决定下一步"""
-    print(f"\n正在分析 Level {level_data['level']}...")
-
-    # 构造输入
-    analysis_input = f"""
-<原始问题>
-{original_question}
-</原始问题>
-
-<已探索的所有层级>
-{json.dumps(all_levels, ensure_ascii=False, indent=2)}
-</已探索的所有层级>
-
-<当前层级>
-Level {level_data['level']}
-{json.dumps(level_data['queries'], ensure_ascii=False, indent=2)}
-</当前层级>
-
-请分析当前探索状态,决定下一步行动。
-"""
-
-    result = await Runner.run(level_analyzer, analysis_input)
-    analysis: LevelAnalysis = result.final_output
-
-    print(f"\n分析结果:")
-    print(f"  关键发现:{analysis.key_findings}")
-    print(f"  有价值的信号:{len(analysis.promising_signals)} 个")
-    print(f"  是否评估:{analysis.should_evaluate_now}")
-
-    if analysis.should_evaluate_now:
-        print(f"  候选query:{analysis.candidates_to_evaluate}")
-    else:
-        print(f"  下一层探索:{analysis.next_combinations}")
-
-    # 保存分析结果
-    context.level_analyses.append({
-        "level": level_data['level'],
-        "timestamp": datetime.now().isoformat(),
-        "analysis": analysis.model_dump()
-    })
-
-    return analysis
-
-
-async def evaluate_candidates(candidates: list[str], original_question: str, context: RunContext) -> list[dict]:
-    """评估候选query"""
-    print(f"\n{'='*60}")
-    print(f"评估 {len(candidates)} 个候选query")
-    print(f"{'='*60}")
-
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-
-    async def evaluate_single_candidate(candidate: str):
-        print(f"\n评估候选:{candidate}")
-
-        # 1. 获取推荐词
-        suggestions = xiaohongshu_api.get_recommendations(keyword=candidate)
-        print(f"  获取到 {len(suggestions) if suggestions else 0} 个推荐词")
-
-        if not suggestions:
-            return {
-                "candidate": candidate,
-                "suggestions": [],
-                "evaluations": []
-            }
-
-        # 2. 评估每个推荐词
-        async def eval_single_sug(sug: str):
-            eval_input = f"""
-<原始问题>
-{original_question}
-</原始问题>
-
-<待评估的推荐query>
-{sug}
-</待评估的推荐query>
-
-请评估该推荐query的三个分数:
-1. essence_score: 本质/意图是否一致(0或1)
-2. hard_score: 硬性约束是否满足(0或1)
-3. soft_score: 软性修饰保留程度(0-1)
-4. reason: 详细的评估理由
-"""
-            result = await Runner.run(evaluator, eval_input)
-            evaluation: EvaluationFeedback = result.final_output
-            return {
-                "query": sug,
-                "essence_score": evaluation.essence_score,
-                "hard_score": evaluation.hard_score,
-                "soft_score": evaluation.soft_score,
-                "reason": evaluation.reason,
-            }
-
-        evaluations = await asyncio.gather(*[eval_single_sug(s) for s in suggestions])
-
-        return {
-            "candidate": candidate,
-            "suggestions": suggestions,
-            "evaluations": evaluations
-        }
-
-    results = await asyncio.gather(*[evaluate_single_candidate(c) for c in candidates])
-
-    context.evaluation_results = results
-    return results
-
-
-def find_qualified_queries(evaluation_results: list[dict], min_soft_score: float = 0.7) -> list[dict]:
-    """查找所有合格的query"""
-    all_qualified = []
-
-    for result in evaluation_results:
-        for eval_item in result.get("evaluations", []):
-            if (eval_item['essence_score'] == 1
-                and eval_item['hard_score'] == 1
-                and eval_item['soft_score'] >= min_soft_score):
-                all_qualified.append({
-                    "from_candidate": result["candidate"],
-                    **eval_item
-                })
-
-    # 按soft_score降序排列
-    return sorted(all_qualified, key=lambda x: x['soft_score'], reverse=True)
-
-
-# ============================================================================
-# 主流程
-# ============================================================================
-
-async def progressive_exploration(context: RunContext, max_levels: int = 4) -> dict:
-    """
-    渐进式广度探索流程
-
-    Args:
-        context: 运行上下文
-        max_levels: 最大探索层数,默认4
-
-    返回格式:
-    {
-        "success": True/False,
-        "results": [...],
-        "message": "..."
-    }
-    """
-
-    # 阶段1:提取关键词(从原始问题提取)
-    keyword_result = await extract_keywords(context.q)
-    context.keywords = keyword_result.keywords
-
-    # 阶段2:渐进式探索
-    current_level = 1
-
-    # Level 1:单个关键词
-    level_1_queries = context.keywords[:7]  # 限制最多7个关键词
-    level_1_data = await explore_level(level_1_queries, current_level, context)
-
-    # 分析Level 1
-    analysis_1 = await analyze_level(level_1_data, context.exploration_levels, context.q, context)
-
-    if analysis_1.should_evaluate_now:
-        # 直接评估
-        eval_results = await evaluate_candidates(analysis_1.candidates_to_evaluate, context.q, context)
-        qualified = find_qualified_queries(eval_results, min_soft_score=0.7)
-
-        if qualified:
-            return {
-                "success": True,
-                "results": qualified,
-                "message": f"Level 1 即找到 {len(qualified)} 个合格query"
-            }
-
-    # Level 2 及以后:迭代探索
-    for level_num in range(2, max_levels + 1):
-        # 获取上一层的分析结果
-        prev_analysis: LevelAnalysis = context.level_analyses[-1]["analysis"]
-        prev_analysis = LevelAnalysis(**prev_analysis)  # 转回对象
-
-        if not prev_analysis.next_combinations:
-            print(f"\nLevel {level_num-1} 分析后无需继续探索")
-            break
-
-        # 探索当前层
-        level_data = await explore_level(prev_analysis.next_combinations, level_num, context)
-
-        # 分析当前层
-        analysis = await analyze_level(level_data, context.exploration_levels, context.q, context)
-
-        if analysis.should_evaluate_now:
-            # 评估候选
-            eval_results = await evaluate_candidates(analysis.candidates_to_evaluate, context.q, context)
-            qualified = find_qualified_queries(eval_results, min_soft_score=0.7)
-
-            if qualified:
-                return {
-                    "success": True,
-                    "results": qualified,
-                    "message": f"Level {level_num} 找到 {len(qualified)} 个合格query"
-                }
-
-    # 所有层探索完,降低标准
-    print(f"\n{'='*60}")
-    print(f"探索完 {max_levels} 层,降低标准(soft_score >= 0.5)")
-    print(f"{'='*60}")
-
-    if context.evaluation_results:
-        acceptable = find_qualified_queries(context.evaluation_results, min_soft_score=0.5)
-        if acceptable:
-            return {
-                "success": True,
-                "results": acceptable,
-                "message": f"找到 {len(acceptable)} 个可接受query(soft_score >= 0.5)"
-            }
-
-    # 完全失败
-    return {
-        "success": False,
-        "results": [],
-        "message": "探索完所有层级,未找到合格的推荐词"
-    }
-
-
-# ============================================================================
-# 输出格式化
-# ============================================================================
-
-def format_output(optimization_result: dict, context: RunContext) -> str:
-    """格式化输出结果"""
-    results = optimization_result.get("results", [])
-
-    output = f"原始问题:{context.q}\n"
-    output += f"提取的关键词:{', '.join(context.keywords or [])}\n"
-    output += f"探索层数:{len(context.exploration_levels)}\n"
-    output += f"状态:{optimization_result['message']}\n\n"
-
-    if optimization_result["success"] and results:
-        output += "合格的推荐query(按soft_score降序):\n"
-        for i, result in enumerate(results, 1):
-            output += f"\n{i}. {result['query']}\n"
-            output += f"   - 来自候选:{result['from_candidate']}\n"
-            output += f"   - 本质匹配度:{result['essence_score']} (1=本质一致)\n"
-            output += f"   - 硬性约束匹配度:{result['hard_score']} (1=所有约束满足)\n"
-            output += f"   - 软性修饰完整度:{result['soft_score']:.2f} (0-1)\n"
-            output += f"   - 评估理由:{result['reason']}\n"
-    else:
-        output += "结果:未找到合格推荐query\n"
-        if context.level_analyses:
-            last_analysis = context.level_analyses[-1]["analysis"]
-            output += f"\n最后一层分析:\n{last_analysis.get('key_findings', 'N/A')}\n"
-
-    return output.strip()
-
-
-# ============================================================================
-# 主函数
-# ============================================================================
-
-async def main(input_dir: str, max_levels: int = 4):
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]
-
-    # 日志保存目录
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-
-    # 执行渐进式探索
-    optimization_result = await progressive_exploration(run_context, max_levels=max_levels)
-
-    # 格式化输出
-    final_output = format_output(optimization_result, run_context)
-    print(f"\n{'='*60}")
-    print("最终结果")
-    print(f"{'='*60}")
-    print(final_output)
-
-    # 保存结果
-    run_context.optimization_result = optimization_result
-    run_context.final_output = final_output
-
-    # 保存 RunContext 到 log_dir
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具 - 渐进式广度探索版")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    parser.add_argument(
-        "--max-levels",
-        type=int,
-        default=4,
-        help="最大探索层数,默认: 4"
-    )
-    args = parser.parse_args()
-
-    asyncio.run(main(args.input_dir, max_levels=args.max_levels))

+ 0 - 824
sug_v6_1_2.py

@@ -1,824 +0,0 @@
-import asyncio
-import json
-import os
-import argparse
-from datetime import datetime
-
-from agents import Agent, Runner
-from lib.my_trace import set_trace
-from typing import Literal
-from pydantic import BaseModel, Field
-
-from lib.utils import read_file_as_string
-from lib.client import get_model
-MODEL_NAME = "google/gemini-2.5-flash"
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-
-    # 探索阶段记录
-    keywords: list[str] | None = Field(default=None, description="提取的关键词")
-    exploration_levels: list[dict] = Field(default_factory=list, description="每一层的探索结果")
-    level_analyses: list[dict] = Field(default_factory=list, description="每一层的主Agent分析")
-
-    # 最终结果
-    final_candidates: list[str] | None = Field(default=None, description="最终选出的候选query")
-    evaluation_results: list[dict] | None = Field(default=None, description="候选query的评估结果")
-    optimization_result: dict | None = Field(default=None, description="最终优化结果对象")
-    final_output: str | None = Field(default=None, description="最终输出结果(格式化文本)")
-
-
-# ============================================================================
-# Agent 1: 关键词提取专家
-# ============================================================================
-keyword_extraction_instructions = """
-你是关键词提取专家。给定一个搜索问题(含上下文),提取出**最细粒度的关键概念**。
-
-## 提取原则
-
-1. **细粒度优先**:拆分成最小的有意义单元
-   - 不要保留完整的长句
-   - 拆分成独立的、有搜索意义的词或短语
-
-2. **保留核心维度**:
-   - 地域/对象
-   - 时间
-   - 行为/意图:获取、教程、推荐、如何等
-   - 主题/领域
-   - 质量/属性
-
-3. **去掉无意义的虚词**:的、吗、呢等
-
-4. **保留领域专有词**:不要过度拆分专业术语
-   - 如果是常见的组合词,保持完整
-
-## 输出要求
-
-输出关键词列表,按重要性排序(最核心的在前)。
-""".strip()
-
-class KeywordList(BaseModel):
-    """关键词列表"""
-    keywords: list[str] = Field(..., description="提取的关键词,按重要性排序")
-    reasoning: str = Field(..., description="提取理由")
-
-keyword_extractor = Agent[None](
-    name="关键词提取专家",
-    instructions=keyword_extraction_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=KeywordList,
-)
-
-
-# ============================================================================
-# Agent 2: 层级探索分析专家
-# ============================================================================
-level_analysis_instructions = """
-你是搜索空间探索分析专家。基于当前层级的探索结果,决定下一步行动。
-
-## 你的任务
-
-分析当前已探索的词汇空间,判断:
-1. **发现了什么有价值的信号?**
-2. **是否已经可以评估候选了?**
-3. **如果还不够,下一层应该探索什么组合?**
-
-## 分析维度
-
-### 1. 信号识别(最重要)
-
-看推荐词里**出现了什么主题**:
-
-**关键问题:**
-- 哪些推荐词**最接近原始需求**?
-- 哪些推荐词**揭示了有价值的方向**(即使不完全匹配)?
-- 哪些推荐词可以作为**下一层探索的桥梁**?
-- 系统对哪些概念理解得好?哪些理解偏了?
-
-### 2. 组合策略
-
-基于发现的信号,设计下一层探索:
-
-**组合类型:**
-
-a) **关键词直接组合**
-   - 两个关键词组合成新query
-
-b) **利用推荐词作为桥梁**(重要!)
-   - 发现某个推荐词很有价值 → 直接探索这个推荐词
-   - 或在推荐词基础上加其他关键词
-
-c) **跨层级组合**
-   - 结合多层发现的有价值推荐词
-   - 组合成更复杂的query
-
-### 3. 停止条件
-
-**何时可以评估候选?**
-
-满足以下之一:
-- 推荐词中出现了**明确包含原始需求多个核心要素的query**
-- 已经探索到**足够复杂的组合**(3-4个关键词),且推荐词相关
-- 探索了**3-4层**,信息已经足够丰富
-
-**何时继续探索?**
-- 当前推荐词太泛,没有接近原始需求
-- 发现了有价值的信号,但需要进一步组合验证
-- 层数还少(< 3层)
-
-## 输出要求
-
-### 1. key_findings
-总结当前层发现的关键信息,包括:
-- 哪些推荐词最有价值?
-- 系统对哪些概念理解得好/不好?
-- 发现了什么意外的方向?
-
-### 2. promising_signals
-列出最有价值的推荐词(来自任何已探索的query),每个说明为什么有价值
-
-### 3. should_evaluate_now
-是否已经可以开始评估候选了?true/false
-
-### 4. candidates_to_evaluate
-如果should_evaluate_now=true,列出应该评估的候选query
-- 可以是推荐词
-- 可以是自己构造的组合
-
-### 5. next_combinations
-如果should_evaluate_now=false,列出下一层应该探索的query组合
-
-### 6. reasoning
-详细的推理过程
-
-## 重要原则
-
-1. **不要过早评估**:至少探索2层,除非第一层就发现了完美匹配
-2. **充分利用推荐词**:推荐词是系统给的提示,要善用
-3. **保持探索方向的多样性**:不要只盯着一个方向
-4. **识别死胡同**:如果某个方向的推荐词一直不相关,果断放弃
-""".strip()
-
-class PromisingSignal(BaseModel):
-    """有价值的推荐词信号"""
-    query: str = Field(..., description="推荐词")
-    from_level: int = Field(..., description="来自哪一层")
-    reason: str = Field(..., description="为什么有价值")
-
-class LevelAnalysis(BaseModel):
-    """层级分析结果"""
-    key_findings: str = Field(..., description="当前层的关键发现")
-    promising_signals: list[PromisingSignal] = Field(..., description="有价值的推荐词信号")
-    should_evaluate_now: bool = Field(..., description="是否应该开始评估候选")
-    candidates_to_evaluate: list[str] = Field(default_factory=list, description="如果should_evaluate_now=true,要评估的候选query列表")
-    next_combinations: list[str] = Field(default_factory=list, description="如果should_evaluate_now=false,下一层要探索的query组合")
-    reasoning: str = Field(..., description="详细的推理过程")
-
-level_analyzer = Agent[None](
-    name="层级探索分析专家",
-    instructions=level_analysis_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=LevelAnalysis,
-)
-
-
-# ============================================================================
-# Agent 3: 评估专家(简化版:意图匹配 + 相关性评分)
-# ============================================================================
-eval_instructions = """
-你是搜索query评估专家。给定原始问题和推荐query,评估两个维度。
-
-## 评估目标
-
-用这个推荐query搜索,能否找到满足原始需求的内容?
-
-## 两层评分
-
-### 1. intent_match(意图匹配)= true/false
-
-推荐query的**使用意图**是否与原问题一致?
-
-**核心问题:用户搜索这个推荐词,想做什么?**
-
-**判断标准:**
-- 原问题意图:找方法?找教程?找资源/素材?找工具?看作品?
-- 推荐词意图:如果用户搜索这个词,他的目的是什么?
-
-**示例:**
-- 原问题意图="找素材"
-  - ✅ true: "素材下载"、"素材网站"、"免费素材"(都是获取素材)
-  - ❌ false: "素材制作教程"、"如何制作素材"(意图变成学习了)
-
-- 原问题意图="学教程"
-  - ✅ true: "教程视频"、"教学步骤"、"入门指南"
-  - ❌ false: "成品展示"、"作品欣赏"(意图变成看作品了)
-
-**评分:**
-- true = 意图一致,搜索推荐词能达到原问题的目的
-- false = 意图改变,搜索推荐词无法达到原问题的目的
-
-### 2. relevance_score(相关性)= 0-1 连续分数
-
-推荐query在**主题、要素、属性**上与原问题的相关程度?
-
-**评估维度:**
-- 主题相关:核心主题是否匹配?(如:摄影、旅游、美食)
-- 要素覆盖:关键要素保留了多少?(如:地域、时间、对象、工具)
-- 属性匹配:质量、风格、特色等属性是否保留?
-
-**评分参考:**
-- 0.9-1.0 = 几乎完美匹配,所有核心要素都保留
-- 0.7-0.8 = 高度相关,核心要素保留,少数次要要素缺失
-- 0.5-0.6 = 中度相关,主题匹配但多个要素缺失
-- 0.3-0.4 = 低度相关,只有部分主题相关
-- 0-0.2 = 基本不相关
-
-## 评估策略
-
-1. **先判断 intent_match**:意图不匹配直接 false,无论相关性多高
-2. **再评估 relevance_score**:在意图匹配的前提下,计算相关性
-
-## 输出要求
-
-- intent_match: true/false
-- relevance_score: 0-1 的浮点数
-- reason: 详细的评估理由,需要说明:
-  - 原问题的意图是什么
-  - 推荐词的意图是什么
-  - 为什么判断意图匹配/不匹配
-  - 相关性分数的依据(哪些要素保留/缺失)
-""".strip()
-
-class RelevanceEvaluation(BaseModel):
-    """评估反馈模型 - 意图匹配 + 相关性"""
-    intent_match: bool = Field(..., description="意图是否匹配")
-    relevance_score: float = Field(..., description="相关性分数 0-1,分数越高越相关")
-    reason: str = Field(..., description="评估理由,需说明意图判断和相关性依据")
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=RelevanceEvaluation,
-)
-
-
-# ============================================================================
-# 核心函数
-# ============================================================================
-
-async def extract_keywords(q: str) -> KeywordList:
-    """提取关键词"""
-    print("\n正在提取关键词...")
-    result = await Runner.run(keyword_extractor, q)
-    keyword_list: KeywordList = result.final_output
-    print(f"提取的关键词:{keyword_list.keywords}")
-    print(f"提取理由:{keyword_list.reasoning}")
-    return keyword_list
-
-
-async def explore_level(queries: list[str], level_num: int, context: RunContext) -> dict:
-    """探索一个层级(并发获取所有query的推荐词)"""
-    print(f"\n{'='*60}")
-    print(f"Level {level_num} 探索:{len(queries)} 个query")
-    print(f"{'='*60}")
-
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-
-    # 并发获取所有推荐词
-    async def get_single_sug(query: str):
-        print(f"  探索: {query}")
-        suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-        print(f"    → {len(suggestions) if suggestions else 0} 个推荐词")
-        return {
-            "query": query,
-            "suggestions": suggestions or []
-        }
-
-    results = await asyncio.gather(*[get_single_sug(q) for q in queries])
-
-    level_data = {
-        "level": level_num,
-        "timestamp": datetime.now().isoformat(),
-        "queries": results
-    }
-
-    context.exploration_levels.append(level_data)
-    return level_data
-
-
-async def analyze_level(level_data: dict, all_levels: list[dict], original_question: str, context: RunContext) -> LevelAnalysis:
-    """分析当前层级,决定下一步"""
-    print(f"\n正在分析 Level {level_data['level']}...")
-
-    # 构造输入
-    analysis_input = f"""
-<原始问题>
-{original_question}
-</原始问题>
-
-<已探索的所有层级>
-{json.dumps(all_levels, ensure_ascii=False, indent=2)}
-</已探索的所有层级>
-
-<当前层级>
-Level {level_data['level']}
-{json.dumps(level_data['queries'], ensure_ascii=False, indent=2)}
-</当前层级>
-
-请分析当前探索状态,决定下一步行动。
-"""
-
-    result = await Runner.run(level_analyzer, analysis_input)
-    analysis: LevelAnalysis = result.final_output
-
-    print(f"\n分析结果:")
-    print(f"  关键发现:{analysis.key_findings}")
-    print(f"  有价值的信号:{len(analysis.promising_signals)} 个")
-    print(f"  是否评估:{analysis.should_evaluate_now}")
-
-    if analysis.should_evaluate_now:
-        print(f"  候选query:{analysis.candidates_to_evaluate}")
-    else:
-        print(f"  下一层探索:{analysis.next_combinations}")
-
-    # 保存分析结果
-    context.level_analyses.append({
-        "level": level_data['level'],
-        "timestamp": datetime.now().isoformat(),
-        "analysis": analysis.model_dump()
-    })
-
-    return analysis
-
-
-async def evaluate_candidates(candidates: list[str], original_question: str, context: RunContext) -> list[dict]:
-    """评估候选query"""
-    print(f"\n{'='*60}")
-    print(f"评估 {len(candidates)} 个候选query")
-    print(f"{'='*60}")
-
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-
-    async def evaluate_single_candidate(candidate: str):
-        print(f"\n评估候选:{candidate}")
-
-        # 1. 获取推荐词
-        suggestions = xiaohongshu_api.get_recommendations(keyword=candidate)
-        print(f"  获取到 {len(suggestions) if suggestions else 0} 个推荐词")
-
-        if not suggestions:
-            return {
-                "candidate": candidate,
-                "suggestions": [],
-                "evaluations": []
-            }
-
-        # 2. 评估每个推荐词
-        async def eval_single_sug(sug: str):
-            eval_input = f"""
-<原始问题>
-{original_question}
-</原始问题>
-
-<待评估的推荐query>
-{sug}
-</待评估的推荐query>
-
-请评估该推荐query:
-1. intent_match: 意图是否匹配(true/false)
-2. relevance_score: 相关性分数(0-1)
-3. reason: 详细的评估理由
-"""
-            result = await Runner.run(evaluator, eval_input)
-            evaluation: RelevanceEvaluation = result.final_output
-            return {
-                "query": sug,
-                "intent_match": evaluation.intent_match,
-                "relevance_score": evaluation.relevance_score,
-                "reason": evaluation.reason,
-            }
-
-        evaluations = await asyncio.gather(*[eval_single_sug(s) for s in suggestions])
-
-        return {
-            "candidate": candidate,
-            "suggestions": suggestions,
-            "evaluations": evaluations
-        }
-
-    results = await asyncio.gather(*[evaluate_single_candidate(c) for c in candidates])
-
-    context.evaluation_results = results
-    return results
-
-
-def find_qualified_queries(evaluation_results: list[dict], min_relevance_score: float = 0.7) -> list[dict]:
-    """
-    查找所有合格的query
-
-    筛选标准:
-    1. intent_match = True(必须满足)
-    2. relevance_score >= min_relevance_score
-
-    返回:按 relevance_score 降序排列
-    """
-    all_qualified = []
-
-    for result in evaluation_results:
-        for eval_item in result.get("evaluations", []):
-            if (eval_item['intent_match'] is True
-                and eval_item['relevance_score'] >= min_relevance_score):
-                all_qualified.append({
-                    "from_candidate": result["candidate"],
-                    **eval_item
-                })
-
-    # 按relevance_score降序排列
-    return sorted(all_qualified, key=lambda x: x['relevance_score'], reverse=True)
-
-
-# ============================================================================
-# 主流程
-# ============================================================================
-
-async def progressive_exploration(context: RunContext, logger, max_levels: int = 4) -> dict:
-    """
-    渐进式广度探索流程
-
-    Args:
-        context: 运行上下文
-        logger: 结构化日志记录器
-        max_levels: 最大探索层数,默认4
-
-    返回格式:
-    {
-        "success": True/False,
-        "results": [...],
-        "message": "..."
-    }
-    """
-
-    # 阶段1:提取关键词(从原始问题提取)
-    step_id = logger.start_step(
-        step_name="提取关键词",
-        step_type="extraction",
-        description="从原始问题提取关键词",
-        input_data={"q": context.q}
-    )
-
-    keyword_result = await extract_keywords(context.q)
-    context.keywords = keyword_result.keywords
-
-    logger.log_artifact(
-        step_id=step_id,
-        artifact_name="keywords",
-        artifact_data=keyword_result.model_dump()
-    )
-
-    logger.end_step(
-        step_id=step_id,
-        output_data={"keywords": keyword_result.keywords, "reasoning": keyword_result.reasoning}
-    )
-
-    # 阶段2:渐进式探索
-    current_level = 1
-
-    # Level 1:单个关键词
-    step_id = logger.start_step(
-        step_name="探索Level 1",
-        step_type="exploration",
-        description=f"探索 Level 1:{len(context.keywords[:7])} 个单关键词",
-        input_data={"queries": context.keywords[:7]}
-    )
-
-    level_1_queries = context.keywords[:7]  # 限制最多7个关键词
-    level_1_data = await explore_level(level_1_queries, current_level, context)
-
-    logger.log_artifact(
-        step_id=step_id,
-        artifact_name="level_1_results",
-        artifact_data=level_1_data
-    )
-
-    logger.end_step(
-        step_id=step_id,
-        output_data={"level": 1, "queries_count": len(level_1_queries)}
-    )
-
-    # 分析Level 1
-    step_id = logger.start_step(
-        step_name="分析Level 1",
-        step_type="analysis",
-        description="分析 Level 1 探索结果",
-        input_data={"level": 1, "queries_count": len(level_1_queries)}
-    )
-
-    analysis_1 = await analyze_level(level_1_data, context.exploration_levels, context.q, context)
-
-    logger.log_artifact(
-        step_id=step_id,
-        artifact_name="analysis_result",
-        artifact_data=analysis_1.model_dump()
-    )
-
-    logger.end_step(
-        step_id=step_id,
-        output_data={
-            "should_evaluate_now": analysis_1.should_evaluate_now,
-            "candidates_to_evaluate": analysis_1.candidates_to_evaluate if analysis_1.should_evaluate_now else [],
-            "next_combinations": analysis_1.next_combinations,
-            "key_findings": analysis_1.key_findings,
-            "reasoning": analysis_1.reasoning,
-            "promising_signals": [s.model_dump() for s in analysis_1.promising_signals]
-        }
-    )
-
-    if analysis_1.should_evaluate_now:
-        # 直接评估
-        step_id = logger.start_step(
-            step_name="评估Level 1候选",
-            step_type="evaluation",
-            description=f"评估 Level 1 的 {len(analysis_1.candidates_to_evaluate)} 个候选query",
-            input_data={"candidates": analysis_1.candidates_to_evaluate}
-        )
-
-        eval_results = await evaluate_candidates(analysis_1.candidates_to_evaluate, context.q, context)
-        qualified = find_qualified_queries(eval_results, min_relevance_score=0.7)
-
-        logger.log_artifact(
-            step_id=step_id,
-            artifact_name="evaluation_results",
-            artifact_data=eval_results
-        )
-
-        logger.end_step(
-            step_id=step_id,
-            output_data={"qualified_count": len(qualified), "qualified": qualified}
-        )
-
-        if qualified:
-            return {
-                "success": True,
-                "results": qualified,
-                "message": f"Level 1 即找到 {len(qualified)} 个合格query"
-            }
-
-    # Level 2 及以后:迭代探索
-    for level_num in range(2, max_levels + 1):
-        # 获取上一层的分析结果
-        prev_analysis: LevelAnalysis = context.level_analyses[-1]["analysis"]
-        prev_analysis = LevelAnalysis(**prev_analysis)  # 转回对象
-
-        if not prev_analysis.next_combinations:
-            print(f"\nLevel {level_num-1} 分析后无需继续探索")
-            break
-
-        # 探索当前层
-        step_id = logger.start_step(
-            step_name=f"探索Level {level_num}",
-            step_type="exploration",
-            description=f"探索 Level {level_num}:{len(prev_analysis.next_combinations)} 个组合query",
-            input_data={"queries": prev_analysis.next_combinations}
-        )
-
-        level_data = await explore_level(prev_analysis.next_combinations, level_num, context)
-
-        logger.log_artifact(
-            step_id=step_id,
-            artifact_name=f"level_{level_num}_results",
-            artifact_data=level_data
-        )
-
-        logger.end_step(
-            step_id=step_id,
-            output_data={"level": level_num, "queries_count": len(prev_analysis.next_combinations)}
-        )
-
-        # 分析当前层
-        step_id = logger.start_step(
-            step_name=f"分析Level {level_num}",
-            step_type="analysis",
-            description=f"分析 Level {level_num} 探索结果",
-            input_data={"level": level_num}
-        )
-
-        analysis = await analyze_level(level_data, context.exploration_levels, context.q, context)
-
-        logger.log_artifact(
-            step_id=step_id,
-            artifact_name="analysis_result",
-            artifact_data=analysis.model_dump()
-        )
-
-        logger.end_step(
-            step_id=step_id,
-            output_data={
-                "should_evaluate_now": analysis.should_evaluate_now,
-                "candidates_to_evaluate": analysis.candidates_to_evaluate if analysis.should_evaluate_now else [],
-                "next_combinations": analysis.next_combinations,
-                "key_findings": analysis.key_findings,
-                "reasoning": analysis.reasoning,
-                "promising_signals": [s.model_dump() for s in analysis.promising_signals]
-            }
-        )
-
-        if analysis.should_evaluate_now:
-            # 评估候选
-            step_id = logger.start_step(
-                step_name=f"评估Level {level_num}候选",
-                step_type="evaluation",
-                description=f"评估 Level {level_num} 的 {len(analysis.candidates_to_evaluate)} 个候选query",
-                input_data={"candidates": analysis.candidates_to_evaluate}
-            )
-
-            eval_results = await evaluate_candidates(analysis.candidates_to_evaluate, context.q, context)
-            qualified = find_qualified_queries(eval_results, min_relevance_score=0.7)
-
-            logger.log_artifact(
-                step_id=step_id,
-                artifact_name="evaluation_results",
-                artifact_data=eval_results
-            )
-
-            logger.end_step(
-                step_id=step_id,
-                output_data={"qualified_count": len(qualified), "qualified": qualified}
-            )
-
-            if qualified:
-                return {
-                    "success": True,
-                    "results": qualified,
-                    "message": f"Level {level_num} 找到 {len(qualified)} 个合格query"
-                }
-
-    # 所有层探索完,降低标准
-    print(f"\n{'='*60}")
-    print(f"探索完 {max_levels} 层,降低标准(relevance_score >= 0.5)")
-    print(f"{'='*60}")
-
-    if context.evaluation_results:
-        acceptable = find_qualified_queries(context.evaluation_results, min_relevance_score=0.5)
-        if acceptable:
-            return {
-                "success": True,
-                "results": acceptable,
-                "message": f"找到 {len(acceptable)} 个可接受query(soft_score >= 0.5)"
-            }
-
-    # 完全失败
-    return {
-        "success": False,
-        "results": [],
-        "message": "探索完所有层级,未找到合格的推荐词"
-    }
-
-
-# ============================================================================
-# 输出格式化
-# ============================================================================
-
-def format_output(optimization_result: dict, context: RunContext) -> str:
-    """格式化输出结果"""
-    results = optimization_result.get("results", [])
-
-    output = f"原始问题:{context.q}\n"
-    output += f"提取的关键词:{', '.join(context.keywords or [])}\n"
-    output += f"探索层数:{len(context.exploration_levels)}\n"
-    output += f"状态:{optimization_result['message']}\n\n"
-
-    if optimization_result["success"] and results:
-        output += "合格的推荐query(按relevance_score降序):\n"
-        for i, result in enumerate(results, 1):
-            output += f"\n{i}. {result['query']}\n"
-            output += f"   - 来自候选:{result['from_candidate']}\n"
-            output += f"   - 意图匹配:{result['intent_match']} (True=意图一致)\n"
-            output += f"   - 相关性分数:{result['relevance_score']:.2f} (0-1,越高越相关)\n"
-            output += f"   - 评估理由:{result['reason']}\n"
-    else:
-        output += "结果:未找到合格推荐query\n"
-        if context.level_analyses:
-            last_analysis = context.level_analyses[-1]["analysis"]
-            output += f"\n最后一层分析:\n{last_analysis.get('key_findings', 'N/A')}\n"
-
-    return output.strip()
-
-
-# ============================================================================
-# 主函数
-# ============================================================================
-
-async def main(input_dir: str, max_levels: int = 4):
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]
-
-    # 日志保存目录
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-
-    # === 初始化结构化日志系统 ===
-    from lib.structured_logger import StructuredLogger
-    from sug_v6_1_2_visualizer import visualize_log
-
-    logger = StructuredLogger(log_dir=log_dir, run_id=current_time)
-
-    try:
-        # 执行渐进式探索
-        optimization_result = await progressive_exploration(run_context, logger, max_levels=max_levels)
-
-        # 格式化输出
-        final_output = format_output(optimization_result, run_context)
-        print(f"\n{'='*60}")
-        print("最终结果")
-        print(f"{'='*60}")
-        print(final_output)
-
-        # 保存结果
-        run_context.optimization_result = optimization_result
-        run_context.final_output = final_output
-
-        # 保存 RunContext 到 log_dir(旧版兼容)
-        os.makedirs(run_context.log_dir, exist_ok=True)
-        context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-        with open(context_file_path, "w", encoding="utf-8") as f:
-            json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-        print(f"\nRunContext saved to: {context_file_path}")
-
-        # === 完成结构化日志 ===
-        logger.finalize(
-            final_status="success",
-            final_output=optimization_result
-        )
-
-        # === 生成可视化页面 ===
-        print("\n生成可视化页面...")
-        visualization_path = visualize_log(logger.log_dir)
-        print(f"🎨 可视化页面已生成: {visualization_path}")
-        print(f"   在浏览器中打开查看详细信息")
-
-    except Exception as e:
-        # 出错时也要完成日志记录
-        logger.finalize(
-            final_status="error",
-            final_output={"error": str(e)}
-        )
-        raise
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具 - v6.1 意图匹配+相关性评分版")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    parser.add_argument(
-        "--max-levels",
-        type=int,
-        default=4,
-        help="最大探索层数,默认: 4"
-    )
-    args = parser.parse_args()
-
-    asyncio.run(main(args.input_dir, max_levels=args.max_levels))

+ 0 - 656
sug_v6_1_2_0.py

@@ -1,656 +0,0 @@
-import asyncio
-import json
-import os
-import argparse
-from datetime import datetime
-
-from agents import Agent, Runner
-from lib.my_trace import set_trace
-from typing import Literal
-from pydantic import BaseModel, Field
-
-from lib.utils import read_file_as_string
-from lib.client import get_model
-MODEL_NAME = "google/gemini-2.5-flash"
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-
-    # 探索阶段记录
-    keywords: list[str] | None = Field(default=None, description="提取的关键词")
-    exploration_levels: list[dict] = Field(default_factory=list, description="每一层的探索结果")
-    level_analyses: list[dict] = Field(default_factory=list, description="每一层的主Agent分析")
-
-    # 最终结果
-    final_candidates: list[str] | None = Field(default=None, description="最终选出的候选query")
-    evaluation_results: list[dict] | None = Field(default=None, description="候选query的评估结果")
-    optimization_result: dict | None = Field(default=None, description="最终优化结果对象")
-    final_output: str | None = Field(default=None, description="最终输出结果(格式化文本)")
-
-
-# ============================================================================
-# Agent 1: 关键词提取专家
-# ============================================================================
-keyword_extraction_instructions = """
-你是关键词提取专家。给定一个搜索问题(含上下文),提取出**最细粒度的关键概念**。
-
-## 提取原则
-
-1. **细粒度优先**:拆分成最小的有意义单元
-   - 不要保留完整的长句
-   - 拆分成独立的、有搜索意义的词或短语
-
-2. **保留核心维度**:
-   - 地域/对象
-   - 时间
-   - 行为/意图:获取、教程、推荐、如何等
-   - 主题/领域
-   - 质量/属性
-
-3. **去掉无意义的虚词**:的、吗、呢等
-
-4. **保留领域专有词**:不要过度拆分专业术语
-   - 如果是常见的组合词,保持完整
-
-## 输出要求
-
-输出关键词列表,按重要性排序(最核心的在前)。
-""".strip()
-
-class KeywordList(BaseModel):
-    """关键词列表"""
-    keywords: list[str] = Field(..., description="提取的关键词,按重要性排序")
-    reasoning: str = Field(..., description="提取理由")
-
-keyword_extractor = Agent[None](
-    name="关键词提取专家",
-    instructions=keyword_extraction_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=KeywordList,
-)
-
-
-# ============================================================================
-# Agent 2: 层级探索分析专家
-# ============================================================================
-level_analysis_instructions = """
-你是搜索空间探索分析专家。基于当前层级的探索结果,决定下一步行动。
-
-## 你的任务
-
-分析当前已探索的词汇空间,判断:
-1. **发现了什么有价值的信号?**
-2. **是否已经可以评估候选了?**
-3. **如果还不够,下一层应该探索什么组合?**
-
-## 分析维度
-
-### 1. 信号识别(最重要)
-
-看推荐词里**出现了什么主题**:
-
-**关键问题:**
-- 哪些推荐词**最接近原始需求**?
-- 哪些推荐词**揭示了有价值的方向**(即使不完全匹配)?
-- 哪些推荐词可以作为**下一层探索的桥梁**?
-- 系统对哪些概念理解得好?哪些理解偏了?
-
-### 2. 组合策略
-
-基于发现的信号,设计下一层探索:
-
-**组合类型:**
-
-a) **关键词直接组合**
-   - 两个关键词组合成新query
-
-b) **利用推荐词作为桥梁**(重要!)
-   - 发现某个推荐词很有价值 → 直接探索这个推荐词
-   - 或在推荐词基础上加其他关键词
-
-c) **跨层级组合**
-   - 结合多层发现的有价值推荐词
-   - 组合成更复杂的query
-
-### 3. 停止条件
-
-**何时可以评估候选?**
-
-满足以下之一:
-- 推荐词中出现了**明确包含原始需求多个核心要素的query**
-- 已经探索到**足够复杂的组合**(3-4个关键词),且推荐词相关
-- 探索了**3-4层**,信息已经足够丰富
-
-**何时继续探索?**
-- 当前推荐词太泛,没有接近原始需求
-- 发现了有价值的信号,但需要进一步组合验证
-- 层数还少(< 3层)
-
-## 输出要求
-
-### 1. key_findings
-总结当前层发现的关键信息,包括:
-- 哪些推荐词最有价值?
-- 系统对哪些概念理解得好/不好?
-- 发现了什么意外的方向?
-
-### 2. promising_signals
-列出最有价值的推荐词(来自任何已探索的query),每个说明为什么有价值
-
-### 3. should_evaluate_now
-是否已经可以开始评估候选了?true/false
-
-### 4. candidates_to_evaluate
-如果should_evaluate_now=true,列出应该评估的候选query
-- 可以是推荐词
-- 可以是自己构造的组合
-
-### 5. next_combinations
-如果should_evaluate_now=false,列出下一层应该探索的query组合
-
-### 6. reasoning
-详细的推理过程
-
-## 重要原则
-
-1. **不要过早评估**:至少探索2层,除非第一层就发现了完美匹配
-2. **充分利用推荐词**:推荐词是系统给的提示,要善用
-3. **保持探索方向的多样性**:不要只盯着一个方向
-4. **识别死胡同**:如果某个方向的推荐词一直不相关,果断放弃
-""".strip()
-
-class PromisingSignal(BaseModel):
-    """有价值的推荐词信号"""
-    query: str = Field(..., description="推荐词")
-    from_level: int = Field(..., description="来自哪一层")
-    reason: str = Field(..., description="为什么有价值")
-
-class LevelAnalysis(BaseModel):
-    """层级分析结果"""
-    key_findings: str = Field(..., description="当前层的关键发现")
-    promising_signals: list[PromisingSignal] = Field(..., description="有价值的推荐词信号")
-    should_evaluate_now: bool = Field(..., description="是否应该开始评估候选")
-    candidates_to_evaluate: list[str] = Field(default_factory=list, description="如果should_evaluate_now=true,要评估的候选query列表")
-    next_combinations: list[str] = Field(default_factory=list, description="如果should_evaluate_now=false,下一层要探索的query组合")
-    reasoning: str = Field(..., description="详细的推理过程")
-
-level_analyzer = Agent[None](
-    name="层级探索分析专家",
-    instructions=level_analysis_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=LevelAnalysis,
-)
-
-
-# ============================================================================
-# Agent 3: 评估专家(简化版:意图匹配 + 相关性评分)
-# ============================================================================
-eval_instructions = """
-你是搜索query评估专家。给定原始问题和推荐query,评估两个维度。
-
-## 评估目标
-
-用这个推荐query搜索,能否找到满足原始需求的内容?
-
-## 两层评分
-
-### 1. intent_match(意图匹配)= true/false
-
-推荐query的**使用意图**是否与原问题一致?
-
-**核心问题:用户搜索这个推荐词,想做什么?**
-
-**判断标准:**
-- 原问题意图:找方法?找教程?找资源/素材?找工具?看作品?
-- 推荐词意图:如果用户搜索这个词,他的目的是什么?
-
-**示例:**
-- 原问题意图="找素材"
-  - ✅ true: "素材下载"、"素材网站"、"免费素材"(都是获取素材)
-  - ❌ false: "素材制作教程"、"如何制作素材"(意图变成学习了)
-
-- 原问题意图="学教程"
-  - ✅ true: "教程视频"、"教学步骤"、"入门指南"
-  - ❌ false: "成品展示"、"作品欣赏"(意图变成看作品了)
-
-**评分:**
-- true = 意图一致,搜索推荐词能达到原问题的目的
-- false = 意图改变,搜索推荐词无法达到原问题的目的
-
-### 2. relevance_score(相关性)= 0-1 连续分数
-
-推荐query在**主题、要素、属性**上与原问题的相关程度?
-
-**评估维度:**
-- 主题相关:核心主题是否匹配?(如:摄影、旅游、美食)
-- 要素覆盖:关键要素保留了多少?(如:地域、时间、对象、工具)
-- 属性匹配:质量、风格、特色等属性是否保留?
-
-**评分参考:**
-- 0.9-1.0 = 几乎完美匹配,所有核心要素都保留
-- 0.7-0.8 = 高度相关,核心要素保留,少数次要要素缺失
-- 0.5-0.6 = 中度相关,主题匹配但多个要素缺失
-- 0.3-0.4 = 低度相关,只有部分主题相关
-- 0-0.2 = 基本不相关
-
-## 评估策略
-
-1. **先判断 intent_match**:意图不匹配直接 false,无论相关性多高
-2. **再评估 relevance_score**:在意图匹配的前提下,计算相关性
-
-## 输出要求
-
-- intent_match: true/false
-- relevance_score: 0-1 的浮点数
-- reason: 详细的评估理由,需要说明:
-  - 原问题的意图是什么
-  - 推荐词的意图是什么
-  - 为什么判断意图匹配/不匹配
-  - 相关性分数的依据(哪些要素保留/缺失)
-""".strip()
-
-class RelevanceEvaluation(BaseModel):
-    """评估反馈模型 - 意图匹配 + 相关性"""
-    intent_match: bool = Field(..., description="意图是否匹配")
-    relevance_score: float = Field(..., description="相关性分数 0-1,分数越高越相关")
-    reason: str = Field(..., description="评估理由,需说明意图判断和相关性依据")
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=RelevanceEvaluation,
-)
-
-
-# ============================================================================
-# 核心函数
-# ============================================================================
-
-async def extract_keywords(q: str) -> KeywordList:
-    """提取关键词"""
-    print("\n正在提取关键词...")
-    result = await Runner.run(keyword_extractor, q)
-    keyword_list: KeywordList = result.final_output
-    print(f"提取的关键词:{keyword_list.keywords}")
-    print(f"提取理由:{keyword_list.reasoning}")
-    return keyword_list
-
-
-async def explore_level(queries: list[str], level_num: int, context: RunContext) -> dict:
-    """探索一个层级(并发获取所有query的推荐词)"""
-    print(f"\n{'='*60}")
-    print(f"Level {level_num} 探索:{len(queries)} 个query")
-    print(f"{'='*60}")
-
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-
-    # 并发获取所有推荐词
-    async def get_single_sug(query: str):
-        print(f"  探索: {query}")
-        suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-        print(f"    → {len(suggestions) if suggestions else 0} 个推荐词")
-        return {
-            "query": query,
-            "suggestions": suggestions or []
-        }
-
-    results = await asyncio.gather(*[get_single_sug(q) for q in queries])
-
-    level_data = {
-        "level": level_num,
-        "timestamp": datetime.now().isoformat(),
-        "queries": results
-    }
-
-    context.exploration_levels.append(level_data)
-    return level_data
-
-
-async def analyze_level(level_data: dict, all_levels: list[dict], original_question: str, context: RunContext) -> LevelAnalysis:
-    """分析当前层级,决定下一步"""
-    print(f"\n正在分析 Level {level_data['level']}...")
-
-    # 构造输入
-    analysis_input = f"""
-<原始问题>
-{original_question}
-</原始问题>
-
-<已探索的所有层级>
-{json.dumps(all_levels, ensure_ascii=False, indent=2)}
-</已探索的所有层级>
-
-<当前层级>
-Level {level_data['level']}
-{json.dumps(level_data['queries'], ensure_ascii=False, indent=2)}
-</当前层级>
-
-请分析当前探索状态,决定下一步行动。
-"""
-
-    result = await Runner.run(level_analyzer, analysis_input)
-    analysis: LevelAnalysis = result.final_output
-
-    print(f"\n分析结果:")
-    print(f"  关键发现:{analysis.key_findings}")
-    print(f"  有价值的信号:{len(analysis.promising_signals)} 个")
-    print(f"  是否评估:{analysis.should_evaluate_now}")
-
-    if analysis.should_evaluate_now:
-        print(f"  候选query:{analysis.candidates_to_evaluate}")
-    else:
-        print(f"  下一层探索:{analysis.next_combinations}")
-
-    # 保存分析结果
-    context.level_analyses.append({
-        "level": level_data['level'],
-        "timestamp": datetime.now().isoformat(),
-        "analysis": analysis.model_dump()
-    })
-
-    return analysis
-
-
-async def evaluate_candidates(candidates: list[str], original_question: str, context: RunContext) -> list[dict]:
-    """评估候选query"""
-    print(f"\n{'='*60}")
-    print(f"评估 {len(candidates)} 个候选query")
-    print(f"{'='*60}")
-
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-
-    async def evaluate_single_candidate(candidate: str):
-        print(f"\n评估候选:{candidate}")
-
-        # 1. 获取推荐词
-        suggestions = xiaohongshu_api.get_recommendations(keyword=candidate)
-        print(f"  获取到 {len(suggestions) if suggestions else 0} 个推荐词")
-
-        if not suggestions:
-            return {
-                "candidate": candidate,
-                "suggestions": [],
-                "evaluations": []
-            }
-
-        # 2. 评估每个推荐词
-        async def eval_single_sug(sug: str):
-            eval_input = f"""
-<原始问题>
-{original_question}
-</原始问题>
-
-<待评估的推荐query>
-{sug}
-</待评估的推荐query>
-
-请评估该推荐query:
-1. intent_match: 意图是否匹配(true/false)
-2. relevance_score: 相关性分数(0-1)
-3. reason: 详细的评估理由
-"""
-            result = await Runner.run(evaluator, eval_input)
-            evaluation: RelevanceEvaluation = result.final_output
-            return {
-                "query": sug,
-                "intent_match": evaluation.intent_match,
-                "relevance_score": evaluation.relevance_score,
-                "reason": evaluation.reason,
-            }
-
-        evaluations = await asyncio.gather(*[eval_single_sug(s) for s in suggestions])
-
-        return {
-            "candidate": candidate,
-            "suggestions": suggestions,
-            "evaluations": evaluations
-        }
-
-    results = await asyncio.gather(*[evaluate_single_candidate(c) for c in candidates])
-
-    context.evaluation_results = results
-    return results
-
-
-def find_qualified_queries(evaluation_results: list[dict], min_relevance_score: float = 0.7) -> list[dict]:
-    """
-    查找所有合格的query
-
-    筛选标准:
-    1. intent_match = True(必须满足)
-    2. relevance_score >= min_relevance_score
-
-    返回:按 relevance_score 降序排列
-    """
-    all_qualified = []
-
-    for result in evaluation_results:
-        for eval_item in result.get("evaluations", []):
-            if (eval_item['intent_match'] is True
-                and eval_item['relevance_score'] >= min_relevance_score):
-                all_qualified.append({
-                    "from_candidate": result["candidate"],
-                    **eval_item
-                })
-
-    # 按relevance_score降序排列
-    return sorted(all_qualified, key=lambda x: x['relevance_score'], reverse=True)
-
-
-# ============================================================================
-# 主流程
-# ============================================================================
-
-async def progressive_exploration(context: RunContext, max_levels: int = 4) -> dict:
-    """
-    渐进式广度探索流程
-
-    Args:
-        context: 运行上下文
-        max_levels: 最大探索层数,默认4
-
-    返回格式:
-    {
-        "success": True/False,
-        "results": [...],
-        "message": "..."
-    }
-    """
-
-    # 阶段1:提取关键词(从原始问题提取)
-    keyword_result = await extract_keywords(context.q)
-    context.keywords = keyword_result.keywords
-
-    # 阶段2:渐进式探索
-    current_level = 1
-
-    # Level 1:单个关键词
-    level_1_queries = context.keywords[:7]  # 限制最多7个关键词
-    level_1_data = await explore_level(level_1_queries, current_level, context)
-
-    # 分析Level 1
-    analysis_1 = await analyze_level(level_1_data, context.exploration_levels, context.q, context)
-
-    if analysis_1.should_evaluate_now:
-        # 直接评估
-        eval_results = await evaluate_candidates(analysis_1.candidates_to_evaluate, context.q, context)
-        qualified = find_qualified_queries(eval_results, min_relevance_score=0.7)
-
-        if qualified:
-            return {
-                "success": True,
-                "results": qualified,
-                "message": f"Level 1 即找到 {len(qualified)} 个合格query"
-            }
-
-    # Level 2 及以后:迭代探索
-    for level_num in range(2, max_levels + 1):
-        # 获取上一层的分析结果
-        prev_analysis: LevelAnalysis = context.level_analyses[-1]["analysis"]
-        prev_analysis = LevelAnalysis(**prev_analysis)  # 转回对象
-
-        if not prev_analysis.next_combinations:
-            print(f"\nLevel {level_num-1} 分析后无需继续探索")
-            break
-
-        # 探索当前层
-        level_data = await explore_level(prev_analysis.next_combinations, level_num, context)
-
-        # 分析当前层
-        analysis = await analyze_level(level_data, context.exploration_levels, context.q, context)
-
-        if analysis.should_evaluate_now:
-            # 评估候选
-            eval_results = await evaluate_candidates(analysis.candidates_to_evaluate, context.q, context)
-            qualified = find_qualified_queries(eval_results, min_relevance_score=0.7)
-
-            if qualified:
-                return {
-                    "success": True,
-                    "results": qualified,
-                    "message": f"Level {level_num} 找到 {len(qualified)} 个合格query"
-                }
-
-    # 所有层探索完,降低标准
-    print(f"\n{'='*60}")
-    print(f"探索完 {max_levels} 层,降低标准(relevance_score >= 0.5)")
-    print(f"{'='*60}")
-
-    if context.evaluation_results:
-        acceptable = find_qualified_queries(context.evaluation_results, min_relevance_score=0.5)
-        if acceptable:
-            return {
-                "success": True,
-                "results": acceptable,
-                "message": f"找到 {len(acceptable)} 个可接受query(soft_score >= 0.5)"
-            }
-
-    # 完全失败
-    return {
-        "success": False,
-        "results": [],
-        "message": "探索完所有层级,未找到合格的推荐词"
-    }
-
-
-# ============================================================================
-# 输出格式化
-# ============================================================================
-
-def format_output(optimization_result: dict, context: RunContext) -> str:
-    """格式化输出结果"""
-    results = optimization_result.get("results", [])
-
-    output = f"原始问题:{context.q}\n"
-    output += f"提取的关键词:{', '.join(context.keywords or [])}\n"
-    output += f"探索层数:{len(context.exploration_levels)}\n"
-    output += f"状态:{optimization_result['message']}\n\n"
-
-    if optimization_result["success"] and results:
-        output += "合格的推荐query(按relevance_score降序):\n"
-        for i, result in enumerate(results, 1):
-            output += f"\n{i}. {result['query']}\n"
-            output += f"   - 来自候选:{result['from_candidate']}\n"
-            output += f"   - 意图匹配:{result['intent_match']} (True=意图一致)\n"
-            output += f"   - 相关性分数:{result['relevance_score']:.2f} (0-1,越高越相关)\n"
-            output += f"   - 评估理由:{result['reason']}\n"
-    else:
-        output += "结果:未找到合格推荐query\n"
-        if context.level_analyses:
-            last_analysis = context.level_analyses[-1]["analysis"]
-            output += f"\n最后一层分析:\n{last_analysis.get('key_findings', 'N/A')}\n"
-
-    return output.strip()
-
-
-# ============================================================================
-# 主函数
-# ============================================================================
-
-async def main(input_dir: str, max_levels: int = 4):
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]
-
-    # 日志保存目录
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-
-    # 执行渐进式探索
-    optimization_result = await progressive_exploration(run_context, max_levels=max_levels)
-
-    # 格式化输出
-    final_output = format_output(optimization_result, run_context)
-    print(f"\n{'='*60}")
-    print("最终结果")
-    print(f"{'='*60}")
-    print(final_output)
-
-    # 保存结果
-    run_context.optimization_result = optimization_result
-    run_context.final_output = final_output
-
-    # 保存 RunContext 到 log_dir
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具 - v6.1 意图匹配+相关性评分版")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    parser.add_argument(
-        "--max-levels",
-        type=int,
-        default=4,
-        help="最大探索层数,默认: 4"
-    )
-    args = parser.parse_args()
-
-    asyncio.run(main(args.input_dir, max_levels=args.max_levels))

+ 0 - 811
sug_v6_1_2_1.py

@@ -1,811 +0,0 @@
-import asyncio
-import json
-import os
-import argparse
-from datetime import datetime
-
-from agents import Agent, Runner
-from lib.my_trace import set_trace
-from typing import Literal
-from pydantic import BaseModel, Field
-
-from lib.utils import read_file_as_string
-from lib.client import get_model
-MODEL_NAME = "google/gemini-2.5-flash"
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-
-    # 步骤化日志
-    steps: list[dict] = Field(default_factory=list, description="执行步骤的详细记录")
-
-    # 探索阶段记录(保留用于向后兼容)
-    keywords: list[str] | None = Field(default=None, description="提取的关键词")
-    exploration_levels: list[dict] = Field(default_factory=list, description="每一层的探索结果")
-    level_analyses: list[dict] = Field(default_factory=list, description="每一层的主Agent分析")
-
-    # 最终结果
-    final_candidates: list[str] | None = Field(default=None, description="最终选出的候选query")
-    evaluation_results: list[dict] | None = Field(default=None, description="候选query的评估结果")
-    optimization_result: dict | None = Field(default=None, description="最终优化结果对象")
-    final_output: str | None = Field(default=None, description="最终输出结果(格式化文本)")
-
-
-# ============================================================================
-# Agent 1: 关键词提取专家
-# ============================================================================
-keyword_extraction_instructions = """
-你是关键词提取专家。给定一个搜索问题(含上下文),提取出**最细粒度的关键概念**。
-
-## 提取原则
-
-1. **细粒度优先**:拆分成最小的有意义单元
-   - 不要保留完整的长句
-   - 拆分成独立的、有搜索意义的词或短语
-
-2. **保留核心维度**:
-   - 地域/对象
-   - 时间
-   - 行为/意图:获取、教程、推荐、如何等
-   - 主题/领域
-   - 质量/属性
-
-3. **去掉无意义的虚词**:的、吗、呢等
-
-4. **保留领域专有词**:不要过度拆分专业术语
-   - 如果是常见的组合词,保持完整
-
-## 输出要求
-
-输出关键词列表,按重要性排序(最核心的在前)。
-""".strip()
-
-class KeywordList(BaseModel):
-    """关键词列表"""
-    keywords: list[str] = Field(..., description="提取的关键词,按重要性排序")
-    reasoning: str = Field(..., description="提取理由")
-
-keyword_extractor = Agent[None](
-    name="关键词提取专家",
-    instructions=keyword_extraction_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=KeywordList,
-)
-
-
-# ============================================================================
-# Agent 2: 层级探索分析专家
-# ============================================================================
-level_analysis_instructions = """
-你是搜索空间探索分析专家。基于当前层级的探索结果,决定下一步行动。
-
-## 你的任务
-
-分析当前已探索的词汇空间,判断:
-1. **发现了什么有价值的信号?**
-2. **是否已经可以评估候选了?**
-3. **如果还不够,下一层应该探索什么组合?**
-
-## 分析维度
-
-### 1. 信号识别(最重要)
-
-看推荐词里**出现了什么主题**:
-
-**关键问题:**
-- 哪些推荐词**最接近原始需求**?
-- 哪些推荐词**揭示了有价值的方向**(即使不完全匹配)?
-- 哪些推荐词可以作为**下一层探索的桥梁**?
-- 系统对哪些概念理解得好?哪些理解偏了?
-
-### 2. 组合策略
-
-基于发现的信号,设计下一层探索:
-
-**组合类型:**
-
-a) **关键词直接组合**
-   - 两个关键词组合成新query
-
-b) **利用推荐词作为桥梁**(重要!)
-   - 发现某个推荐词很有价值 → 直接探索这个推荐词
-   - 或在推荐词基础上加其他关键词
-
-c) **跨层级组合**
-   - 结合多层发现的有价值推荐词
-   - 组合成更复杂的query
-
-### 3. 停止条件
-
-**何时可以评估候选?**
-
-满足以下之一:
-- 推荐词中出现了**明确包含原始需求多个核心要素的query**
-- 已经探索到**足够复杂的组合**(3-4个关键词),且推荐词相关
-- 探索了**3-4层**,信息已经足够丰富
-
-**何时继续探索?**
-- 当前推荐词太泛,没有接近原始需求
-- 发现了有价值的信号,但需要进一步组合验证
-- 层数还少(< 3层)
-
-## 输出要求
-
-### 1. key_findings
-总结当前层发现的关键信息,包括:
-- 哪些推荐词最有价值?
-- 系统对哪些概念理解得好/不好?
-- 发现了什么意外的方向?
-
-### 2. promising_signals
-列出最有价值的推荐词(来自任何已探索的query),每个说明为什么有价值
-
-### 3. should_evaluate_now
-是否已经可以开始评估候选了?true/false
-
-### 4. candidates_to_evaluate
-如果should_evaluate_now=true,列出应该评估的候选query
-- 可以是推荐词
-- 可以是自己构造的组合
-
-### 5. next_combinations
-如果should_evaluate_now=false,列出下一层应该探索的query组合
-
-### 6. reasoning
-详细的推理过程
-
-## 重要原则
-
-1. **不要过早评估**:至少探索2层,除非第一层就发现了完美匹配
-2. **充分利用推荐词**:推荐词是系统给的提示,要善用
-3. **保持探索方向的多样性**:不要只盯着一个方向
-4. **识别死胡同**:如果某个方向的推荐词一直不相关,果断放弃
-""".strip()
-
-class PromisingSignal(BaseModel):
-    """有价值的推荐词信号"""
-    query: str = Field(..., description="推荐词")
-    from_level: int = Field(..., description="来自哪一层")
-    reason: str = Field(..., description="为什么有价值")
-
-class LevelAnalysis(BaseModel):
-    """层级分析结果"""
-    key_findings: str = Field(..., description="当前层的关键发现")
-    promising_signals: list[PromisingSignal] = Field(..., description="有价值的推荐词信号")
-    should_evaluate_now: bool = Field(..., description="是否应该开始评估候选")
-    candidates_to_evaluate: list[str] = Field(default_factory=list, description="如果should_evaluate_now=true,要评估的候选query列表")
-    next_combinations: list[str] = Field(default_factory=list, description="如果should_evaluate_now=false,下一层要探索的query组合")
-    reasoning: str = Field(..., description="详细的推理过程")
-
-level_analyzer = Agent[None](
-    name="层级探索分析专家",
-    instructions=level_analysis_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=LevelAnalysis,
-)
-
-
-# ============================================================================
-# Agent 3: 评估专家(简化版:意图匹配 + 相关性评分)
-# ============================================================================
-eval_instructions = """
-你是搜索query评估专家。给定原始问题和推荐query,评估两个维度。
-
-## 评估目标
-
-用这个推荐query搜索,能否找到满足原始需求的内容?
-
-## 两层评分
-
-### 1. intent_match(意图匹配)= true/false
-
-推荐query的**使用意图**是否与原问题一致?
-
-**核心问题:用户搜索这个推荐词,想做什么?**
-
-**判断标准:**
-- 原问题意图:找方法?找教程?找资源/素材?找工具?看作品?
-- 推荐词意图:如果用户搜索这个词,他的目的是什么?
-
-**示例:**
-- 原问题意图="找素材"
-  - ✅ true: "素材下载"、"素材网站"、"免费素材"(都是获取素材)
-  - ❌ false: "素材制作教程"、"如何制作素材"(意图变成学习了)
-
-- 原问题意图="学教程"
-  - ✅ true: "教程视频"、"教学步骤"、"入门指南"
-  - ❌ false: "成品展示"、"作品欣赏"(意图变成看作品了)
-
-**评分:**
-- true = 意图一致,搜索推荐词能达到原问题的目的
-- false = 意图改变,搜索推荐词无法达到原问题的目的
-
-### 2. relevance_score(相关性)= 0-1 连续分数
-
-推荐query在**主题、要素、属性**上与原问题的相关程度?
-
-**评估维度:**
-- 主题相关:核心主题是否匹配?(如:摄影、旅游、美食)
-- 要素覆盖:关键要素保留了多少?(如:地域、时间、对象、工具)
-- 属性匹配:质量、风格、特色等属性是否保留?
-
-**评分参考:**
-- 0.9-1.0 = 几乎完美匹配,所有核心要素都保留
-- 0.7-0.8 = 高度相关,核心要素保留,少数次要要素缺失
-- 0.5-0.6 = 中度相关,主题匹配但多个要素缺失
-- 0.3-0.4 = 低度相关,只有部分主题相关
-- 0-0.2 = 基本不相关
-
-## 评估策略
-
-1. **先判断 intent_match**:意图不匹配直接 false,无论相关性多高
-2. **再评估 relevance_score**:在意图匹配的前提下,计算相关性
-
-## 输出要求
-
-- intent_match: true/false
-- relevance_score: 0-1 的浮点数
-- reason: 详细的评估理由,需要说明:
-  - 原问题的意图是什么
-  - 推荐词的意图是什么
-  - 为什么判断意图匹配/不匹配
-  - 相关性分数的依据(哪些要素保留/缺失)
-""".strip()
-
-class RelevanceEvaluation(BaseModel):
-    """评估反馈模型 - 意图匹配 + 相关性"""
-    intent_match: bool = Field(..., description="意图是否匹配")
-    relevance_score: float = Field(..., description="相关性分数 0-1,分数越高越相关")
-    reason: str = Field(..., description="评估理由,需说明意图判断和相关性依据")
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=RelevanceEvaluation,
-)
-
-
-# ============================================================================
-# 日志辅助函数
-# ============================================================================
-
-def add_step(context: RunContext, step_name: str, step_type: str, data: dict):
-    """添加步骤记录"""
-    step = {
-        "step_number": len(context.steps) + 1,
-        "step_name": step_name,
-        "step_type": step_type,
-        "timestamp": datetime.now().isoformat(),
-        "data": data
-    }
-    context.steps.append(step)
-    return step
-
-
-# ============================================================================
-# 核心函数
-# ============================================================================
-
-async def extract_keywords(q: str, context: RunContext) -> KeywordList:
-    """提取关键词"""
-    print("\n[步骤 1] 正在提取关键词...")
-    result = await Runner.run(keyword_extractor, q)
-    keyword_list: KeywordList = result.final_output
-    print(f"提取的关键词:{keyword_list.keywords}")
-    print(f"提取理由:{keyword_list.reasoning}")
-
-    # 记录步骤
-    add_step(context, "提取关键词", "keyword_extraction", {
-        "input_question": q,
-        "keywords": keyword_list.keywords,
-        "reasoning": keyword_list.reasoning
-    })
-
-    return keyword_list
-
-
-async def explore_level(queries: list[str], level_num: int, context: RunContext) -> dict:
-    """探索一个层级(并发获取所有query的推荐词)"""
-    step_num = len(context.steps) + 1
-    print(f"\n{'='*60}")
-    print(f"[步骤 {step_num}] Level {level_num} 探索:{len(queries)} 个query")
-    print(f"{'='*60}")
-
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-
-    # 并发获取所有推荐词
-    async def get_single_sug(query: str):
-        print(f"  探索: {query}")
-        suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-        print(f"    → {len(suggestions) if suggestions else 0} 个推荐词")
-        return {
-            "query": query,
-            "suggestions": suggestions or []
-        }
-
-    results = await asyncio.gather(*[get_single_sug(q) for q in queries])
-
-    level_data = {
-        "level": level_num,
-        "timestamp": datetime.now().isoformat(),
-        "queries": results
-    }
-
-    context.exploration_levels.append(level_data)
-
-    # 记录步骤
-    add_step(context, f"Level {level_num} 探索", "level_exploration", {
-        "level": level_num,
-        "input_queries": queries,
-        "query_count": len(queries),
-        "results": results,
-        "total_suggestions": sum(len(r['suggestions']) for r in results)
-    })
-
-    return level_data
-
-
-async def analyze_level(level_data: dict, all_levels: list[dict], original_question: str, context: RunContext) -> LevelAnalysis:
-    """分析当前层级,决定下一步"""
-    step_num = len(context.steps) + 1
-    print(f"\n[步骤 {step_num}] 正在分析 Level {level_data['level']}...")
-
-    # 构造输入
-    analysis_input = f"""
-<原始问题>
-{original_question}
-</原始问题>
-
-<已探索的所有层级>
-{json.dumps(all_levels, ensure_ascii=False, indent=2)}
-</已探索的所有层级>
-
-<当前层级>
-Level {level_data['level']}
-{json.dumps(level_data['queries'], ensure_ascii=False, indent=2)}
-</当前层级>
-
-请分析当前探索状态,决定下一步行动。
-"""
-
-    result = await Runner.run(level_analyzer, analysis_input)
-    analysis: LevelAnalysis = result.final_output
-
-    print(f"\n分析结果:")
-    print(f"  关键发现:{analysis.key_findings}")
-    print(f"  有价值的信号:{len(analysis.promising_signals)} 个")
-    print(f"  是否评估:{analysis.should_evaluate_now}")
-
-    if analysis.should_evaluate_now:
-        print(f"  候选query:{analysis.candidates_to_evaluate}")
-    else:
-        print(f"  下一层探索:{analysis.next_combinations}")
-
-    # 保存分析结果
-    context.level_analyses.append({
-        "level": level_data['level'],
-        "timestamp": datetime.now().isoformat(),
-        "analysis": analysis.model_dump()
-    })
-
-    # 记录步骤
-    add_step(context, f"Level {level_data['level']} 分析", "level_analysis", {
-        "level": level_data['level'],
-        "key_findings": analysis.key_findings,
-        "promising_signals_count": len(analysis.promising_signals),
-        "promising_signals": [s.model_dump() for s in analysis.promising_signals],
-        "should_evaluate_now": analysis.should_evaluate_now,
-        "candidates_to_evaluate": analysis.candidates_to_evaluate if analysis.should_evaluate_now else [],
-        "next_combinations": analysis.next_combinations if not analysis.should_evaluate_now else [],
-        "reasoning": analysis.reasoning
-    })
-
-    return analysis
-
-
-async def evaluate_candidates(candidates: list[str], original_question: str, context: RunContext) -> list[dict]:
-    """评估候选query"""
-    step_num = len(context.steps) + 1
-    print(f"\n{'='*60}")
-    print(f"[步骤 {step_num}] 评估 {len(candidates)} 个候选query")
-    print(f"{'='*60}")
-
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-
-    async def evaluate_single_candidate(candidate: str):
-        print(f"\n评估候选:{candidate}")
-
-        # 1. 获取推荐词
-        suggestions = xiaohongshu_api.get_recommendations(keyword=candidate)
-        print(f"  获取到 {len(suggestions) if suggestions else 0} 个推荐词")
-
-        if not suggestions:
-            return {
-                "candidate": candidate,
-                "suggestions": [],
-                "evaluations": []
-            }
-
-        # 2. 评估每个推荐词
-        async def eval_single_sug(sug: str):
-            eval_input = f"""
-<原始问题>
-{original_question}
-</原始问题>
-
-<待评估的推荐query>
-{sug}
-</待评估的推荐query>
-
-请评估该推荐query:
-1. intent_match: 意图是否匹配(true/false)
-2. relevance_score: 相关性分数(0-1)
-3. reason: 详细的评估理由
-"""
-            result = await Runner.run(evaluator, eval_input)
-            evaluation: RelevanceEvaluation = result.final_output
-            return {
-                "query": sug,
-                "intent_match": evaluation.intent_match,
-                "relevance_score": evaluation.relevance_score,
-                "reason": evaluation.reason,
-            }
-
-        evaluations = await asyncio.gather(*[eval_single_sug(s) for s in suggestions])
-
-        return {
-            "candidate": candidate,
-            "suggestions": suggestions,
-            "evaluations": evaluations
-        }
-
-    results = await asyncio.gather(*[evaluate_single_candidate(c) for c in candidates])
-
-    context.evaluation_results = results
-
-    # 记录步骤
-    add_step(context, "评估候选query", "candidate_evaluation", {
-        "candidate_count": len(candidates),
-        "candidates": candidates,
-        "results": results,
-        "total_evaluations": sum(len(r['evaluations']) for r in results)
-    })
-
-    return results
-
-
-def find_qualified_queries(evaluation_results: list[dict], min_relevance_score: float = 0.7) -> list[dict]:
-    """
-    查找所有合格的query
-
-    筛选标准:
-    1. intent_match = True(必须满足)
-    2. relevance_score >= min_relevance_score
-
-    返回:按 relevance_score 降序排列
-    """
-    all_qualified = []
-
-    for result in evaluation_results:
-        for eval_item in result.get("evaluations", []):
-            if (eval_item['intent_match'] is True
-                and eval_item['relevance_score'] >= min_relevance_score):
-                all_qualified.append({
-                    "from_candidate": result["candidate"],
-                    **eval_item
-                })
-
-    # 按relevance_score降序排列
-    return sorted(all_qualified, key=lambda x: x['relevance_score'], reverse=True)
-
-
-# ============================================================================
-# 主流程
-# ============================================================================
-
-async def progressive_exploration(context: RunContext, max_levels: int = 4) -> dict:
-    """
-    渐进式广度探索流程
-
-    Args:
-        context: 运行上下文
-        max_levels: 最大探索层数,默认4
-
-    返回格式:
-    {
-        "success": True/False,
-        "results": [...],
-        "message": "..."
-    }
-    """
-
-    # 阶段1:提取关键词(从原始问题提取)
-    keyword_result = await extract_keywords(context.q, context)
-    context.keywords = keyword_result.keywords
-
-    # 阶段2:渐进式探索
-    current_level = 1
-
-    # Level 1:单个关键词
-    level_1_queries = context.keywords[:7]  # 限制最多7个关键词
-    level_1_data = await explore_level(level_1_queries, current_level, context)
-
-    # 分析Level 1
-    analysis_1 = await analyze_level(level_1_data, context.exploration_levels, context.q, context)
-
-    if analysis_1.should_evaluate_now:
-        # 直接评估
-        eval_results = await evaluate_candidates(analysis_1.candidates_to_evaluate, context.q, context)
-        qualified = find_qualified_queries(eval_results, min_relevance_score=0.7)
-
-        if qualified:
-            return {
-                "success": True,
-                "results": qualified,
-                "message": f"Level 1 即找到 {len(qualified)} 个合格query"
-            }
-
-    # Level 2 及以后:迭代探索
-    for level_num in range(2, max_levels + 1):
-        # 获取上一层的分析结果
-        prev_analysis: LevelAnalysis = context.level_analyses[-1]["analysis"]
-        prev_analysis = LevelAnalysis(**prev_analysis)  # 转回对象
-
-        if not prev_analysis.next_combinations:
-            print(f"\nLevel {level_num-1} 分析后无需继续探索")
-            break
-
-        # 探索当前层
-        level_data = await explore_level(prev_analysis.next_combinations, level_num, context)
-
-        # 分析当前层
-        analysis = await analyze_level(level_data, context.exploration_levels, context.q, context)
-
-        if analysis.should_evaluate_now:
-            # 评估候选
-            eval_results = await evaluate_candidates(analysis.candidates_to_evaluate, context.q, context)
-            qualified = find_qualified_queries(eval_results, min_relevance_score=0.7)
-
-            if qualified:
-                return {
-                    "success": True,
-                    "results": qualified,
-                    "message": f"Level {level_num} 找到 {len(qualified)} 个合格query"
-                }
-
-    # 所有层探索完,降低标准
-    print(f"\n{'='*60}")
-    print(f"探索完 {max_levels} 层,降低标准(relevance_score >= 0.5)")
-    print(f"{'='*60}")
-
-    if context.evaluation_results:
-        acceptable = find_qualified_queries(context.evaluation_results, min_relevance_score=0.5)
-        if acceptable:
-            return {
-                "success": True,
-                "results": acceptable,
-                "message": f"找到 {len(acceptable)} 个可接受query(soft_score >= 0.5)"
-            }
-
-    # 完全失败
-    return {
-        "success": False,
-        "results": [],
-        "message": "探索完所有层级,未找到合格的推荐词"
-    }
-
-
-# ============================================================================
-# 输出格式化
-# ============================================================================
-
-def format_output(optimization_result: dict, context: RunContext) -> str:
-    """格式化输出结果"""
-    results = optimization_result.get("results", [])
-
-    output = f"原始问题:{context.q}\n"
-    output += f"提取的关键词:{', '.join(context.keywords or [])}\n"
-    output += f"探索层数:{len(context.exploration_levels)}\n"
-    output += f"状态:{optimization_result['message']}\n\n"
-
-    if optimization_result["success"] and results:
-        output += "合格的推荐query(按relevance_score降序):\n"
-        for i, result in enumerate(results, 1):
-            output += f"\n{i}. {result['query']}\n"
-            output += f"   - 来自候选:{result['from_candidate']}\n"
-            output += f"   - 意图匹配:{result['intent_match']} (True=意图一致)\n"
-            output += f"   - 相关性分数:{result['relevance_score']:.2f} (0-1,越高越相关)\n"
-            output += f"   - 评估理由:{result['reason']}\n"
-    else:
-        output += "结果:未找到合格推荐query\n"
-        if context.level_analyses:
-            last_analysis = context.level_analyses[-1]["analysis"]
-            output += f"\n最后一层分析:\n{last_analysis.get('key_findings', 'N/A')}\n"
-
-    return output.strip()
-
-
-# ============================================================================
-# 主函数
-# ============================================================================
-
-async def main(input_dir: str, max_levels: int = 4):
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]
-
-    # 日志保存目录
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-
-    # 执行渐进式探索
-    optimization_result = await progressive_exploration(run_context, max_levels=max_levels)
-
-    # 格式化输出
-    final_output = format_output(optimization_result, run_context)
-    print(f"\n{'='*60}")
-    print("最终结果")
-    print(f"{'='*60}")
-    print(final_output)
-
-    # 保存结果
-    run_context.optimization_result = optimization_result
-    run_context.final_output = final_output
-
-    # 记录最终输出步骤(保存完整的结果详情)
-    qualified_results = optimization_result.get("results", [])
-    add_step(run_context, "生成最终结果", "final_result", {
-        "success": optimization_result["success"],
-        "message": optimization_result["message"],
-        "qualified_query_count": len(qualified_results),
-        "qualified_queries": [r["query"] for r in qualified_results],  # 保存所有合格query
-        "qualified_results_detail": [  # 保存完整的评估详情
-            {
-                "rank": idx + 1,
-                "query": r["query"],
-                "from_candidate": r["from_candidate"],
-                "intent_match": r["intent_match"],
-                "relevance_score": r["relevance_score"],
-                "reason": r["reason"]
-            }
-            for idx, r in enumerate(qualified_results)
-        ],
-        "final_output": final_output
-    })
-
-    # 保存 RunContext 到 log_dir
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-    # 保存步骤化日志(更直观的格式)
-    steps_file_path = os.path.join(run_context.log_dir, "steps.json")
-    with open(steps_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.steps, f, ensure_ascii=False, indent=2)
-    print(f"Steps log saved to: {steps_file_path}")
-
-    # 生成步骤化的可读文本日志
-    steps_text_path = os.path.join(run_context.log_dir, "steps.md")
-    with open(steps_text_path, "w", encoding="utf-8") as f:
-        f.write(f"# 执行步骤日志\n\n")
-        f.write(f"**原始问题**: {run_context.q}\n\n")
-        f.write(f"**执行版本**: {run_context.version}\n\n")
-        f.write(f"**总步骤数**: {len(run_context.steps)}\n\n")
-        f.write("---\n\n")
-
-        for step in run_context.steps:
-            f.write(f"## 步骤 {step['step_number']}: {step['step_name']}\n\n")
-            f.write(f"**类型**: `{step['step_type']}`\n\n")
-            f.write(f"**时间**: {step['timestamp']}\n\n")
-
-            # 根据不同类型格式化数据
-            if step['step_type'] == 'keyword_extraction':
-                f.write(f"**提取的关键词**: {', '.join(step['data']['keywords'])}\n\n")
-                f.write(f"**提取理由**: {step['data']['reasoning']}\n\n")
-
-            elif step['step_type'] == 'level_exploration':
-                f.write(f"**探索层级**: Level {step['data']['level']}\n\n")
-                f.write(f"**输入query数量**: {step['data']['query_count']}\n\n")
-                f.write(f"**总推荐词数**: {step['data']['total_suggestions']}\n\n")
-                f.write(f"**探索的query**: {', '.join(step['data']['input_queries'])}\n\n")
-
-            elif step['step_type'] == 'level_analysis':
-                f.write(f"**关键发现**: {step['data']['key_findings']}\n\n")
-                f.write(f"**有价值信号数**: {step['data']['promising_signals_count']}\n\n")
-                f.write(f"**是否评估**: {step['data']['should_evaluate_now']}\n\n")
-                if step['data']['should_evaluate_now']:
-                    f.write(f"**候选query**: {', '.join(step['data']['candidates_to_evaluate'])}\n\n")
-                else:
-                    f.write(f"**下一层探索**: {', '.join(step['data']['next_combinations'])}\n\n")
-
-            elif step['step_type'] == 'candidate_evaluation':
-                f.write(f"**评估候选数**: {step['data']['candidate_count']}\n\n")
-                f.write(f"**候选query**: {', '.join(step['data']['candidates'])}\n\n")
-                f.write(f"**总评估数**: {step['data']['total_evaluations']}\n\n")
-
-            elif step['step_type'] == 'final_result':
-                f.write(f"**执行状态**: {'✅ 成功' if step['data']['success'] else '❌ 失败'}\n\n")
-                f.write(f"**结果消息**: {step['data']['message']}\n\n")
-                f.write(f"**合格query数量**: {step['data']['qualified_query_count']}\n\n")
-
-                # 显示详细的评估结果
-                if step['data'].get('qualified_results_detail'):
-                    f.write(f"### 合格的query详情\n\n")
-                    for result in step['data']['qualified_results_detail']:
-                        f.write(f"#### {result['rank']}. {result['query']}\n\n")
-                        f.write(f"- **来自候选**: {result['from_candidate']}\n")
-                        f.write(f"- **意图匹配**: {'✅ 是' if result['intent_match'] else '❌ 否'}\n")
-                        f.write(f"- **相关性分数**: {result['relevance_score']:.2f}\n")
-                        f.write(f"- **评估理由**: {result['reason']}\n\n")
-                elif step['data']['qualified_queries']:
-                    # 兼容旧格式(如果没有详情)
-                    f.write(f"**合格的query列表**:\n")
-                    for idx, q in enumerate(step['data']['qualified_queries'], 1):
-                        f.write(f"  {idx}. {q}\n")
-                    f.write("\n")
-
-                f.write(f"### 完整输出\n\n```\n{step['data']['final_output']}\n```\n\n")
-
-            f.write("---\n\n")
-
-    print(f"Steps markdown saved to: {steps_text_path}")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具 - v6.1 意图匹配+相关性评分版")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    parser.add_argument(
-        "--max-levels",
-        type=int,
-        default=4,
-        help="最大探索层数,默认: 4"
-    )
-    args = parser.parse_args()
-
-    asyncio.run(main(args.input_dir, max_levels=args.max_levels))

+ 1061 - 0
sug_v6_1_2_10.py

@@ -0,0 +1,1061 @@
+import asyncio
+import json
+import os
+import sys
+import argparse
+from datetime import datetime
+from typing import Literal
+
+from agents import Agent, Runner
+from lib.my_trace import set_trace
+from pydantic import BaseModel, Field
+
+from lib.utils import read_file_as_string
+from lib.client import get_model
+MODEL_NAME = "google/gemini-2.5-flash"
+from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
+from script.search.xiaohongshu_search import XiaohongshuSearch
+
+
+# ============================================================================
+# 数据模型
+# ============================================================================
+
+class Seg(BaseModel):
+    """分词"""
+    text: str
+    score_with_o: float = 0.0  # 与原始问题的评分
+    reason: str = ""  # 评分理由
+    from_o: str = ""  # 原始问题
+
+
+class Word(BaseModel):
+    """词"""
+    text: str
+    score_with_o: float = 0.0  # 与原始问题的评分
+    from_o: str = ""  # 原始问题
+
+
+class QFromQ(BaseModel):
+    """Q来源信息(用于Sug中记录)"""
+    text: str
+    score_with_o: float = 0.0
+
+
+class Q(BaseModel):
+    """查询"""
+    text: str
+    score_with_o: float = 0.0  # 与原始问题的评分
+    reason: str = ""  # 评分理由
+    from_source: str = ""  # seg/sug/add(加词)
+
+
+class Sug(BaseModel):
+    """建议词"""
+    text: str
+    score_with_o: float = 0.0  # 与原始问题的评分
+    reason: str = ""  # 评分理由
+    from_q: QFromQ | None = None  # 来自的q
+
+
+class Seed(BaseModel):
+    """种子"""
+    text: str
+    added_words: list[str] = Field(default_factory=list)  # 已经增加的words
+    from_type: str = ""  # seg/sug
+    score_with_o: float = 0.0  # 与原始问题的评分
+
+
+class Post(BaseModel):
+    """帖子"""
+    title: str = ""
+    body_text: str = ""
+    type: str = "normal"  # video/normal
+    images: list[str] = Field(default_factory=list)  # 图片url列表,第一张为封面
+    video: str = ""  # 视频url
+    interact_info: dict = Field(default_factory=dict)  # 互动信息
+    note_id: str = ""
+    note_url: str = ""
+
+
+class Search(Sug):
+    """搜索结果(继承Sug)"""
+    post_list: list[Post] = Field(default_factory=list)  # 搜索得到的帖子列表
+
+
+class RunContext(BaseModel):
+    """运行上下文"""
+    version: str
+    input_files: dict[str, str]
+    c: str  # 原始需求
+    o: str  # 原始问题
+    log_url: str
+    log_dir: str
+
+    # 每轮的数据
+    rounds: list[dict] = Field(default_factory=list)  # 每轮的详细数据
+
+    # 最终结果
+    final_output: str | None = None
+
+
+# ============================================================================
+# Agent 定义
+# ============================================================================
+
+# Agent 1: 分词专家
+class WordSegmentation(BaseModel):
+    """分词结果"""
+    words: list[str] = Field(..., description="分词结果列表")
+    reasoning: str = Field(..., description="分词理由")
+
+word_segmentation_instructions = """
+你是分词专家。给定一个query,将其拆分成有意义的最小单元。
+
+## 分词原则
+1. 保留有搜索意义的词汇
+2. 拆分成独立的概念
+3. 保留专业术语的完整性
+4. 去除虚词(的、吗、呢等)
+
+## 输出要求
+返回分词列表和分词理由。
+""".strip()
+
+word_segmenter = Agent[None](
+    name="分词专家",
+    instructions=word_segmentation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=WordSegmentation,
+)
+
+
+# Agent 2: 相关度评估专家
+class RelevanceEvaluation(BaseModel):
+    """相关度评估"""
+    reason: str = Field(..., description="评估理由")
+    relevance_score: float = Field(..., description="相关性分数 -1~1")
+
+relevance_evaluation_instructions = """
+# 角色定义
+你是一个 **专业的语言专家和语义相关性评判专家**。你的任务是:判断我给你的 <平台sug词条> 与 <原始问题> 的相关度满足度,给出 **-1 到 1 之间** 的数值评分。
+
+---
+
+# 核心概念与方法论
+
+## 两大评估维度
+本评估系统始终围绕 **两个核心维度** 进行:
+
+### 1. 动机维度(权重70%)
+**定义:** 用户"想要做什么",即原始问题的行为意图和目的
+- 核心是 **动词**:获取、学习、拍摄、制作、寻找等
+- 包括:核心动作 + 使用场景 + 最终目的
+
+### 2. 品类维度(权重30%)
+**定义:** 用户"关于什么内容",即原始问题的主题对象和限定词
+- 核心是 **名词+限定词**:川西秋季风光摄影素材
+- 包括:核心主体 + 地域限定 + 时间限定 + 质量限定等
+
+---
+
+## 如何识别原始问题的核心动机
+
+**核心动机必须是动词**,识别方法如下:
+
+### 方法1: 显性动词直接提取
+
+当原始问题明确包含动词时,直接提取
+示例:
+"如何获取素材" → 核心动机 = "获取"
+"寻找拍摄技巧" → 核心动机 = "寻找"(或"学习")
+"制作视频教程" → 核心动机 = "制作"
+
+### 方法2: 隐性动词语义推理
+
+当原始问题没有显性动词时,需要结合上下文推理
+示例:
+例: "川西秋天风光摄影" → 隐含动作="拍摄"
+→ 需结合上下文判断
+
+如果原始问题是纯名词短语,无任何动作线索:
+→ 核心动机 = 无法识别
+→ 初始权重 = 0
+→ 相关度评估以品类匹配为主
+示例:
+"摄影" → 无法识别动机,初始权重=0
+"川西风光" → 无法识别动机,初始权重=0
+
+
+
+# 输入信息
+你将接收到以下输入:
+- **<原始问题>**:用户的初始查询问题,代表用户的真实需求意图。
+- **<平台sug词条>**:平台推荐的词条列表,每个词条需要单独评估。
+
+
+#判定流程
+#评估架构
+
+输入: <原始问题> + <平台sug词条>
+         ↓
+【综合相关性判定】
+    ├→ 步骤1: 评估<sug词条>与<原始问题>的相关度
+    └→ 输出: -1到1之间的数值 + 分维度得分 + 判定依据
+
+
+相关度评估维度详解
+维度1: 动机维度评估(权重70%)
+评估对象: <平台sug词条> 与 <原始问题> 的需求动机匹配度
+说明: 核心动作是用户需求的第一优先级,决定了推荐的基本有效性
+
+
+评分标准:
+
+【正向匹配】
++1.0: 核心动作完全一致
+  - 例: 原始问题"如何获取素材" vs sug词"素材获取方法"
+  - 特殊规则: 如果sug词的核心动作是原始问题动作的**具体化子集**,也判定为完全一致
+    · 例: 原始问题"扣除猫咪主体的方法" vs sug词"扣除猫咪眼睛的方法"(子集但目的一致)
+
++0.8~0.95: 核心动作语义相近或为同义表达
+  - 例: 原始问题"如何获取素材" vs sug词"素材下载教程"
+  - 同义词对: 获取≈下载≈寻找, 技巧≈方法≈教程≈攻略
+
++0.5~0.75: 核心动作相关但非直接对应(相关实现路径)
+  - 例: 原始问题"如何获取素材" vs sug词"素材管理整理"
+
++0.2~0.45: 核心动作弱相关(同领域不同动作)
+  - 例: 原始问题"如何拍摄风光" vs sug词"风光摄影欣赏"
+
+【中性/无关】
+0: 没有明确目的,动作意图无明确关联
+  - 例: 原始问题"如何获取素材" vs sug词"摄影器材推荐"
+  - 例: 原始问题无法识别动机 且 sug词也无明确动作 → 0
+
+【负向偏离】
+-0.2~-0.05: 动作意图轻度冲突或误导
+  - 例: 原始问题"如何获取素材" vs sug词"素材版权保护须知"
+
+-0.5~-0.25: 动作意图明显对立
+  - 例: 原始问题"如何获取免费素材" vs sug词"如何售卖素材"
+
+-1.0~-0.55: 动作意图完全相反或产生严重负面引导
+  - 例: 原始问题"免费素材获取" vs sug词"付费素材强制推销"
+
+维度2: 品类维度评估(权重30%)
+评估对象: <平台sug词条> 与 <原始问题> 的内容主体和限定词匹配度
+
+评分标准:
+
+【正向匹配】
++1.0: 核心主体+所有关键限定词完全匹配
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"川西秋季风光摄影作品"
+
++0.75~0.95: 核心主体匹配,大部分限定词匹配
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"川西风光摄影素材"(缺失"秋季")
+
++0.5~0.7: 核心主体匹配,少量限定词匹配或合理泛化
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"四川风光摄影"
+
++0.2~0.45: 仅主体词匹配,限定词全部缺失或错位
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"风光摄影入门"
+
++0.05~0.15: 主题领域相关但品类不同
+  - 例: 原始问题"风光摄影素材" vs sug词"人文摄影素材"
+
+【中性/无关】
+0: 主体词部分相关但类别明显不同
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"人像摄影素材"
+
+【负向偏离】
+-0.2~-0.05: 主体词或限定词存在误导性
+  - 例: 原始问题"免费摄影素材" vs sug词"付费摄影素材库"
+
+-0.5~-0.25: 主体词明显错位或品类冲突
+  - 例: 原始问题"风光摄影素材" vs sug词"人像修图教程"
+
+-1.0~-0.55: 完全错误的品类或有害引导
+  - 例: 原始问题"正版素材获取" vs sug词"盗版素材下载"
+
+
+综合得分计算与规则调整
+步骤1: 应用依存性规则
+规则A: 动机高分保护机制
+
+如果 动机维度得分 ≥ 0.8:
+   → 品类得分即使为0或轻微负向(-0.2~0)
+   → 最终得分 = max(初步得分, 0.55)
+
+解释: 当目的高度一致时,品类的泛化不应导致"弱相关"
+
+规则B: 动机低分限制机制
+如果 动机维度得分 ≤ 0.2:
+   → 无论品类得分多高
+   → 最终得分 = min(初步得分, 0.4)
+
+解释: 目的不符时,品类匹配的价值有限
+
+规则C: 动机负向决定机制
+如果 动机维度得分 < 0:
+   → 最终得分 = min(初步得分, 0)
+
+解释: 动作意图冲突时,推荐具有误导性,不应为正相关
+
+步骤3: 输出最终得分
+
+#基础加权计算
+应用规则后的调整得分 = 目的动机维度得分 × 0.7 + 品类维度得分 × 0.3
+取值范围: -1.0 ~ +1.0
+
+---
+
+# 得分档位解释
+
+高度相关】+0.8 ~ +1.0
+相关性高度契合,用户可直接使用
+动机和品类均高度匹配
+典型场景: 动机≥0.85 且 品类≥0.7
+【中高相关】+0.6 ~ +0.79
+相关性较好,用户基本满意
+动机匹配但品类有泛化,或反之
+典型场景: 动机≥0.8 且 品类≥0.3
+【中度相关】+0.3 ~ +0.59
+部分相关,用户需要调整搜索策略
+动机或品类存在一定偏差
+典型场景: 动机0.4-0.7 且 品类0.3-0.7
+【弱相关】+0.01 ~ +0.29
+关联微弱,参考价值有限
+仅有表层词汇重叠
+【无关】0
+无明确关联
+原始问题无法识别动机 且 sug词无明确动作
+没有目的性且没有品类匹配
+【轻度负向】-0.29 ~ -0.01
+产生轻微误导或干扰
+【中度负向】-0.69 ~ -0.3
+存在明显冲突或误导
+【严重负向】-1.0 ~ -0.7
+完全违背意图或产生有害引导
+
+---
+
+# 输出要求
+输出结果必须为一个 **JSON 格式**,包含以下内容:
+
+#注意事项:
+始终围绕两个核心维度:所有评估都基于"动机"和"品类"两个维度,不偏离
+核心动机必须是动词:在评估前,必须先提取原始问题的核心动机(动词),这是整个评估的基础
+严格标准一致性:对所有用例使用相同的评估标准,避免评分飘移
+负分使用原则:仅当sug词条对原始问题产生误导、冲突或有害引导时给予负分
+零分使用原则:当sug词条与原始问题无明确关联,既不相关也不冲突时给予零分
+分维度独立评分:
+先提取原始问题核心动机
+分别计算动机维度(含两个子维度)和品类维度得分
+按70:30加权得到初步得分
+应用规则调整得到最终得分
+动机优先原则:当动机高度一致时,品类的合理泛化或具体化不应导致低评分
+技巧类需求特殊对待:包含"技巧/方法/教程"等词的需求,对动作一致性要求更严格
+
+## 输出
+- reason: 详细理由
+- relevance_score: -1到1的相关性分数
+""".strip()
+
+relevance_evaluator = Agent[None](
+    name="相关度评估专家",
+    instructions=relevance_evaluation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=RelevanceEvaluation,
+)
+
+
+# Agent 3: 加词选择专家
+class WordSelection(BaseModel):
+    """加词选择结果"""
+    selected_word: str = Field(..., description="选择的词")
+    combined_query: str = Field(..., description="组合后的新query")
+    reasoning: str = Field(..., description="选择理由")
+
+word_selection_instructions = """
+你是加词选择专家。
+
+## 任务
+从候选词列表中选择一个最合适的词,与当前seed组合成新的query。
+
+## 原则
+1. 选择与当前seed最相关的词
+2. 组合后的query要语义通顺
+3. 符合搜索习惯
+4. 优先选择能扩展搜索范围的词
+
+## 输出
+- selected_word: 选中的词
+- combined_query: 组合后的新query
+- reasoning: 选择理由
+""".strip()
+
+word_selector = Agent[None](
+    name="加词选择专家",
+    instructions=word_selection_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=WordSelection,
+)
+
+
+# ============================================================================
+# 辅助函数
+# ============================================================================
+
+def process_note_data(note: dict) -> Post:
+    """处理搜索接口返回的帖子数据"""
+    note_card = note.get("note_card", {})
+    image_list = note_card.get("image_list", [])
+    interact_info = note_card.get("interact_info", {})
+    user_info = note_card.get("user", {})
+
+    # 提取图片URL - 使用新的字段名 image_url
+    images = []
+    for img in image_list:
+        if isinstance(img, dict):
+            # 尝试新字段名 image_url,如果不存在则尝试旧字段名 url_default
+            img_url = img.get("image_url") or img.get("url_default")
+            if img_url:
+                images.append(img_url)
+
+    # 判断类型
+    note_type = note_card.get("type", "normal")
+    video_url = ""
+    if note_type == "video":
+        video_info = note_card.get("video", {})
+        if isinstance(video_info, dict):
+            # 尝试获取视频URL
+            video_url = video_info.get("media", {}).get("stream", {}).get("h264", [{}])[0].get("master_url", "")
+
+    return Post(
+        note_id=note.get("id", ""),
+        title=note_card.get("display_title", ""),
+        body_text=note_card.get("desc", ""),
+        type=note_type,
+        images=images,
+        video=video_url,
+        interact_info={
+            "liked_count": interact_info.get("liked_count", 0),
+            "collected_count": interact_info.get("collected_count", 0),
+            "comment_count": interact_info.get("comment_count", 0),
+            "shared_count": interact_info.get("shared_count", 0)
+        },
+        note_url=f"https://www.xiaohongshu.com/explore/{note.get('id', '')}"
+    )
+
+
+async def evaluate_with_o(text: str, o: str) -> tuple[float, str]:
+    """评估文本与原始问题o的相关度
+
+    Returns:
+        tuple[float, str]: (相关度分数, 评估理由)
+    """
+    eval_input = f"""
+<原始问题>
+{o}
+</原始问题>
+
+<当前文本>
+{text}
+</当前文本>
+
+请评估当前文本与原始问题的相关度。
+"""
+    result = await Runner.run(relevance_evaluator, eval_input)
+    evaluation: RelevanceEvaluation = result.final_output
+    return evaluation.relevance_score, evaluation.reason
+
+
+# ============================================================================
+# 核心流程函数
+# ============================================================================
+
+async def initialize(o: str, context: RunContext) -> tuple[list[Seg], list[Word], list[Q], list[Seed]]:
+    """
+    初始化阶段
+
+    Returns:
+        (seg_list, word_list_1, q_list_1, seed_list)
+    """
+    print(f"\n{'='*60}")
+    print(f"初始化阶段")
+    print(f"{'='*60}")
+
+    # 1. 分词:原始问题(o) ->分词-> seg_list
+    print(f"\n[步骤1] 分词...")
+    result = await Runner.run(word_segmenter, o)
+    segmentation: WordSegmentation = result.final_output
+
+    seg_list = []
+    for word in segmentation.words:
+        seg_list.append(Seg(text=word, from_o=o))
+
+    print(f"分词结果: {[s.text for s in seg_list]}")
+    print(f"分词理由: {segmentation.reasoning}")
+
+    # 2. 分词评估:seg_list -> 每个seg与o进行评分(并发)
+    print(f"\n[步骤2] 评估每个分词与原始问题的相关度...")
+
+    async def evaluate_seg(seg: Seg) -> Seg:
+        seg.score_with_o, seg.reason = await evaluate_with_o(seg.text, o)
+        return seg
+
+    if seg_list:
+        eval_tasks = [evaluate_seg(seg) for seg in seg_list]
+        await asyncio.gather(*eval_tasks)
+
+    for seg in seg_list:
+        print(f"  {seg.text}: {seg.score_with_o:.2f}")
+
+    # 3. 构建word_list_1: seg_list -> word_list_1
+    print(f"\n[步骤3] 构建word_list_1...")
+    word_list_1 = []
+    for seg in seg_list:
+        word_list_1.append(Word(
+            text=seg.text,
+            score_with_o=seg.score_with_o,
+            from_o=o
+        ))
+    print(f"word_list_1: {[w.text for w in word_list_1]}")
+
+    # 4. 构建q_list_1:seg_list 作为 q_list_1
+    print(f"\n[步骤4] 构建q_list_1...")
+    q_list_1 = []
+    for seg in seg_list:
+        q_list_1.append(Q(
+            text=seg.text,
+            score_with_o=seg.score_with_o,
+            reason=seg.reason,
+            from_source="seg"
+        ))
+    print(f"q_list_1: {[q.text for q in q_list_1]}")
+
+    # 5. 构建seed_list: seg_list -> seed_list
+    print(f"\n[步骤5] 构建seed_list...")
+    seed_list = []
+    for seg in seg_list:
+        seed_list.append(Seed(
+            text=seg.text,
+            added_words=[],
+            from_type="seg",
+            score_with_o=seg.score_with_o
+        ))
+    print(f"seed_list: {[s.text for s in seed_list]}")
+
+    return seg_list, word_list_1, q_list_1, seed_list
+
+
+async def run_round(
+    round_num: int,
+    q_list: list[Q],
+    word_list: list[Word],
+    seed_list: list[Seed],
+    o: str,
+    context: RunContext,
+    xiaohongshu_api: XiaohongshuSearchRecommendations,
+    xiaohongshu_search: XiaohongshuSearch,
+    sug_threshold: float = 0.7
+) -> tuple[list[Word], list[Q], list[Seed], list[Search]]:
+    """
+    运行一轮
+
+    Args:
+        round_num: 轮次编号
+        q_list: 当前轮的q列表
+        word_list: 当前的word列表
+        seed_list: 当前的seed列表
+        o: 原始问题
+        context: 运行上下文
+        xiaohongshu_api: 建议词API
+        xiaohongshu_search: 搜索API
+        sug_threshold: suggestion的阈值
+
+    Returns:
+        (word_list_next, q_list_next, seed_list_next, search_list)
+    """
+    print(f"\n{'='*60}")
+    print(f"第{round_num}轮")
+    print(f"{'='*60}")
+
+    round_data = {
+        "round_num": round_num,
+        "input_q_list": [{"text": q.text, "score": q.score_with_o} for q in q_list],
+        "input_word_list_size": len(word_list),
+        "input_seed_list_size": len(seed_list)
+    }
+
+    # 1. 请求sug:q_list -> 每个q请求sug接口 -> sug_list_list
+    print(f"\n[步骤1] 为每个q请求建议词...")
+    sug_list_list = []  # list of list
+    for q in q_list:
+        print(f"\n  处理q: {q.text}")
+        suggestions = xiaohongshu_api.get_recommendations(keyword=q.text)
+
+        q_sug_list = []
+        if suggestions:
+            print(f"    获取到 {len(suggestions)} 个建议词")
+            for sug_text in suggestions:
+                sug = Sug(
+                    text=sug_text,
+                    from_q=QFromQ(text=q.text, score_with_o=q.score_with_o)
+                )
+                q_sug_list.append(sug)
+        else:
+            print(f"    未获取到建议词")
+
+        sug_list_list.append(q_sug_list)
+
+    # 2. sug评估:sug_list_list -> 每个sug与o进评分(并发)
+    print(f"\n[步骤2] 评估每个建议词与原始问题的相关度...")
+
+    # 2.1 收集所有需要评估的sug,并记录它们所属的q
+    all_sugs = []
+    sug_to_q_map = {}  # 记录每个sug属于哪个q
+    for i, q_sug_list in enumerate(sug_list_list):
+        if q_sug_list:
+            q_text = q_list[i].text
+            for sug in q_sug_list:
+                all_sugs.append(sug)
+                sug_to_q_map[id(sug)] = q_text
+
+    # 2.2 并发评估所有sug
+    async def evaluate_sug(sug: Sug) -> Sug:
+        sug.score_with_o, sug.reason = await evaluate_with_o(sug.text, o)
+        return sug
+
+    if all_sugs:
+        eval_tasks = [evaluate_sug(sug) for sug in all_sugs]
+        await asyncio.gather(*eval_tasks)
+
+    # 2.3 打印结果并组织到sug_details
+    sug_details = {}  # 保存每个Q对应的sug列表
+    for i, q_sug_list in enumerate(sug_list_list):
+        if q_sug_list:
+            q_text = q_list[i].text
+            print(f"\n  来自q '{q_text}' 的建议词:")
+            sug_details[q_text] = []
+            for sug in q_sug_list:
+                print(f"    {sug.text}: {sug.score_with_o:.2f}")
+                # 保存到sug_details
+                sug_details[q_text].append({
+                    "text": sug.text,
+                    "score": sug.score_with_o,
+                    "reason": sug.reason
+                })
+
+    # 3. search_list构建
+    print(f"\n[步骤3] 构建search_list(阈值>{sug_threshold})...")
+    search_list = []
+    high_score_sugs = [sug for sug in all_sugs if sug.score_with_o > sug_threshold]
+
+    if high_score_sugs:
+        print(f"  找到 {len(high_score_sugs)} 个高分建议词")
+
+        # 并发搜索
+        async def search_for_sug(sug: Sug) -> Search:
+            print(f"    搜索: {sug.text}")
+            try:
+                search_result = xiaohongshu_search.search(keyword=sug.text)
+                result_str = search_result.get("result", "{}")
+                if isinstance(result_str, str):
+                    result_data = json.loads(result_str)
+                else:
+                    result_data = result_str
+
+                notes = result_data.get("data", {}).get("data", [])
+                post_list = []
+                for note in notes[:10]:  # 只取前10个
+                    post = process_note_data(note)
+                    post_list.append(post)
+
+                print(f"      → 找到 {len(post_list)} 个帖子")
+
+                return Search(
+                    text=sug.text,
+                    score_with_o=sug.score_with_o,
+                    from_q=sug.from_q,
+                    post_list=post_list
+                )
+            except Exception as e:
+                print(f"      ✗ 搜索失败: {e}")
+                return Search(
+                    text=sug.text,
+                    score_with_o=sug.score_with_o,
+                    from_q=sug.from_q,
+                    post_list=[]
+                )
+
+        search_tasks = [search_for_sug(sug) for sug in high_score_sugs]
+        search_list = await asyncio.gather(*search_tasks)
+    else:
+        print(f"  没有高分建议词,search_list为空")
+
+    # 4. 构建word_list_next: word_list -> word_list_next(先直接复制)
+    print(f"\n[步骤4] 构建word_list_next(暂时直接复制)...")
+    word_list_next = word_list.copy()
+
+    # 5. 构建q_list_next
+    print(f"\n[步骤5] 构建q_list_next...")
+    q_list_next = []
+    add_word_details = {}  # 保存每个seed对应的组合词列表
+
+    # 5.1 对于seed_list中的每个seed,从word_list_next中选一个未加过的词
+    print(f"\n  5.1 为每个seed加词...")
+    for seed in seed_list:
+        print(f"\n    处理seed: {seed.text}")
+
+        # 简单过滤:找出不在seed.text中且未被添加过的词
+        candidate_words = []
+        for word in word_list_next:
+            # 检查词是否已在seed中
+            if word.text in seed.text:
+                continue
+            # 检查词是否已被添加过
+            if word.text in seed.added_words:
+                continue
+            candidate_words.append(word)
+
+        if not candidate_words:
+            print(f"      没有可用的候选词")
+            continue
+
+        print(f"      候选词: {[w.text for w in candidate_words]}")
+
+        # 使用Agent选择最合适的词
+        selection_input = f"""
+<原始问题>
+{o}
+</原始问题>
+
+<当前Seed>
+{seed.text}
+</当前Seed>
+
+<候选词列表>
+{', '.join([w.text for w in candidate_words])}
+</候选词列表>
+
+请从候选词中选择一个最合适的词,与当前seed组合成新的query。
+"""
+        result = await Runner.run(word_selector, selection_input)
+        selection: WordSelection = result.final_output
+
+        # 验证选择的词是否在候选列表中
+        if selection.selected_word not in [w.text for w in candidate_words]:
+            print(f"      ✗ Agent选择的词 '{selection.selected_word}' 不在候选列表中,跳过")
+            continue
+
+        print(f"      ✓ 选择词: {selection.selected_word}")
+        print(f"      ✓ 新query: {selection.combined_query}")
+        print(f"      理由: {selection.reasoning}")
+
+        # 评估新query
+        new_q_score, new_q_reason = await evaluate_with_o(selection.combined_query, o)
+        print(f"      新query评分: {new_q_score:.2f}")
+
+        # 创建新的q
+        new_q = Q(
+            text=selection.combined_query,
+            score_with_o=new_q_score,
+            reason=new_q_reason,
+            from_source="add"
+        )
+        q_list_next.append(new_q)
+
+        # 更新seed的added_words
+        seed.added_words.append(selection.selected_word)
+
+        # 保存到add_word_details
+        if seed.text not in add_word_details:
+            add_word_details[seed.text] = []
+        add_word_details[seed.text].append({
+            "text": selection.combined_query,
+            "score": new_q_score,
+            "reason": new_q_reason,
+            "selected_word": selection.selected_word
+        })
+
+    # 5.2 对于sug_list_list中,每个sug大于来自的query分数,加到q_list_next
+    print(f"\n  5.2 将高分sug加入q_list_next...")
+    for sug in all_sugs:
+        if sug.from_q and sug.score_with_o > sug.from_q.score_with_o:
+            new_q = Q(
+                text=sug.text,
+                score_with_o=sug.score_with_o,
+                reason=sug.reason,
+                from_source="sug"
+            )
+            q_list_next.append(new_q)
+            print(f"    ✓ {sug.text} (分数: {sug.score_with_o:.2f} > {sug.from_q.score_with_o:.2f})")
+
+    # 6. 更新seed_list
+    print(f"\n[步骤6] 更新seed_list...")
+    seed_list_next = seed_list.copy()  # 保留原有的seed
+
+    # 对于sug_list_list中,每个sug分数大于来源query分数的,且没在seed_list中出现过的,加入
+    existing_seed_texts = {seed.text for seed in seed_list_next}
+    for sug in all_sugs:
+        # 新逻辑:sug分数 > 对应query分数
+        if sug.from_q and sug.score_with_o > sug.from_q.score_with_o and sug.text not in existing_seed_texts:
+            new_seed = Seed(
+                text=sug.text,
+                added_words=[],
+                from_type="sug",
+                score_with_o=sug.score_with_o
+            )
+            seed_list_next.append(new_seed)
+            existing_seed_texts.add(sug.text)
+            print(f"  ✓ 新seed: {sug.text} (分数: {sug.score_with_o:.2f} > 来源query: {sug.from_q.score_with_o:.2f})")
+
+    # 序列化搜索结果数据(包含帖子详情)
+    search_results_data = []
+    for search in search_list:
+        search_results_data.append({
+            "text": search.text,
+            "score_with_o": search.score_with_o,
+            "post_list": [
+                {
+                    "note_id": post.note_id,
+                    "note_url": post.note_url,
+                    "title": post.title,
+                    "body_text": post.body_text,
+                    "images": post.images,
+                    "interact_info": post.interact_info
+                }
+                for post in search.post_list
+            ]
+        })
+
+    # 记录本轮数据
+    round_data.update({
+        "sug_count": len(all_sugs),
+        "high_score_sug_count": len(high_score_sugs),
+        "search_count": len(search_list),
+        "total_posts": sum(len(s.post_list) for s in search_list),
+        "q_list_next_size": len(q_list_next),
+        "seed_list_next_size": len(seed_list_next),
+        "word_list_next_size": len(word_list_next),
+        "output_q_list": [{"text": q.text, "score": q.score_with_o, "reason": q.reason, "from": q.from_source} for q in q_list_next],
+        "seed_list_next": [{"text": seed.text, "from": seed.from_type, "score": seed.score_with_o} for seed in seed_list_next],  # 下一轮种子列表
+        "sug_details": sug_details,  # 每个Q对应的sug列表
+        "add_word_details": add_word_details,  # 每个seed对应的组合词列表
+        "search_results": search_results_data  # 搜索结果(包含帖子详情)
+    })
+    context.rounds.append(round_data)
+
+    print(f"\n本轮总结:")
+    print(f"  建议词数量: {len(all_sugs)}")
+    print(f"  高分建议词: {len(high_score_sugs)}")
+    print(f"  搜索数量: {len(search_list)}")
+    print(f"  帖子总数: {sum(len(s.post_list) for s in search_list)}")
+    print(f"  下轮q数量: {len(q_list_next)}")
+    print(f"  seed数量: {len(seed_list_next)}")
+
+    return word_list_next, q_list_next, seed_list_next, search_list
+
+
+async def iterative_loop(
+    context: RunContext,
+    max_rounds: int = 2,
+    sug_threshold: float = 0.7
+):
+    """主迭代循环"""
+
+    print(f"\n{'='*60}")
+    print(f"开始迭代循环")
+    print(f"最大轮数: {max_rounds}")
+    print(f"sug阈值: {sug_threshold}")
+    print(f"{'='*60}")
+
+    # 初始化
+    seg_list, word_list, q_list, seed_list = await initialize(context.o, context)
+
+    # API实例
+    xiaohongshu_api = XiaohongshuSearchRecommendations()
+    xiaohongshu_search = XiaohongshuSearch()
+
+    # 保存初始化数据
+    context.rounds.append({
+        "round_num": 0,
+        "type": "initialization",
+        "seg_list": [{"text": s.text, "score": s.score_with_o, "reason": s.reason} for s in seg_list],
+        "word_list_1": [{"text": w.text, "score": w.score_with_o} for w in word_list],
+        "q_list_1": [{"text": q.text, "score": q.score_with_o, "reason": q.reason} for q in q_list],
+        "seed_list": [{"text": s.text, "from_type": s.from_type, "score": s.score_with_o} for s in seed_list]
+    })
+
+    # 收集所有搜索结果
+    all_search_list = []
+
+    # 迭代
+    round_num = 1
+    while q_list and round_num <= max_rounds:
+        word_list, q_list, seed_list, search_list = await run_round(
+            round_num=round_num,
+            q_list=q_list,
+            word_list=word_list,
+            seed_list=seed_list,
+            o=context.o,
+            context=context,
+            xiaohongshu_api=xiaohongshu_api,
+            xiaohongshu_search=xiaohongshu_search,
+            sug_threshold=sug_threshold
+        )
+
+        all_search_list.extend(search_list)
+        round_num += 1
+
+    print(f"\n{'='*60}")
+    print(f"迭代完成")
+    print(f"  总轮数: {round_num - 1}")
+    print(f"  总搜索次数: {len(all_search_list)}")
+    print(f"  总帖子数: {sum(len(s.post_list) for s in all_search_list)}")
+    print(f"{'='*60}")
+
+    return all_search_list
+
+
+# ============================================================================
+# 主函数
+# ============================================================================
+
+async def main(input_dir: str, max_rounds: int = 2, sug_threshold: float = 0.7, visualize: bool = False):
+    """主函数"""
+    current_time, log_url = set_trace()
+
+    # 读取输入
+    input_context_file = os.path.join(input_dir, 'context.md')
+    input_q_file = os.path.join(input_dir, 'q.md')
+
+    c = read_file_as_string(input_context_file)  # 原始需求
+    o = read_file_as_string(input_q_file)  # 原始问题
+
+    # 版本信息
+    version = os.path.basename(__file__)
+    version_name = os.path.splitext(version)[0]
+
+    # 日志目录
+    log_dir = os.path.join(input_dir, "output", version_name, current_time)
+
+    # 创建运行上下文
+    run_context = RunContext(
+        version=version,
+        input_files={
+            "input_dir": input_dir,
+            "context_file": input_context_file,
+            "q_file": input_q_file,
+        },
+        c=c,
+        o=o,
+        log_dir=log_dir,
+        log_url=log_url,
+    )
+
+    # 执行迭代
+    all_search_list = await iterative_loop(
+        run_context,
+        max_rounds=max_rounds,
+        sug_threshold=sug_threshold
+    )
+
+    # 格式化输出
+    output = f"原始需求:{run_context.c}\n"
+    output += f"原始问题:{run_context.o}\n"
+    output += f"总搜索次数:{len(all_search_list)}\n"
+    output += f"总帖子数:{sum(len(s.post_list) for s in all_search_list)}\n"
+    output += "\n" + "="*60 + "\n"
+
+    if all_search_list:
+        output += "【搜索结果】\n\n"
+        for idx, search in enumerate(all_search_list, 1):
+            output += f"{idx}. 搜索词: {search.text} (分数: {search.score_with_o:.2f})\n"
+            output += f"   帖子数: {len(search.post_list)}\n"
+            if search.post_list:
+                for post_idx, post in enumerate(search.post_list[:3], 1):  # 只显示前3个
+                    output += f"   {post_idx}) {post.title}\n"
+                    output += f"      URL: {post.note_url}\n"
+            output += "\n"
+    else:
+        output += "未找到搜索结果\n"
+
+    run_context.final_output = output
+
+    print(f"\n{'='*60}")
+    print("最终结果")
+    print(f"{'='*60}")
+    print(output)
+
+    # 保存日志
+    os.makedirs(run_context.log_dir, exist_ok=True)
+
+    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
+    context_dict = run_context.model_dump()
+    with open(context_file_path, "w", encoding="utf-8") as f:
+        json.dump(context_dict, f, ensure_ascii=False, indent=2)
+    print(f"\nRunContext saved to: {context_file_path}")
+
+    # 保存详细的搜索结果
+    search_results_path = os.path.join(run_context.log_dir, "search_results.json")
+    search_results_data = [s.model_dump() for s in all_search_list]
+    with open(search_results_path, "w", encoding="utf-8") as f:
+        json.dump(search_results_data, f, ensure_ascii=False, indent=2)
+    print(f"Search results saved to: {search_results_path}")
+
+    # 可视化
+    if visualize:
+        import subprocess
+        output_html = os.path.join(run_context.log_dir, "visualization.html")
+        print(f"\n🎨 生成可视化HTML...")
+
+        # 获取绝对路径
+        abs_context_file = os.path.abspath(context_file_path)
+        abs_output_html = os.path.abspath(output_html)
+
+        # 运行可视化脚本
+        result = subprocess.run([
+            "node",
+            "visualization/sug_v6_1_2_8/index.js",
+            abs_context_file,
+            abs_output_html
+        ])
+
+        if result.returncode == 0:
+            print(f"✅ 可视化已生成: {output_html}")
+        else:
+            print(f"❌ 可视化生成失败")
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description="搜索query优化工具 - v6.1.2.8 轮次迭代版")
+    parser.add_argument(
+        "--input-dir",
+        type=str,
+        default="input/旅游-逸趣玩旅行/如何获取能体现川西秋季特色的高质量风光摄影素材?",
+        help="输入目录路径,默认: input/旅游-逸趣玩旅行/如何获取能体现川西秋季特色的高质量风光摄影素材?"
+    )
+    parser.add_argument(
+        "--max-rounds",
+        type=int,
+        default=4,
+        help="最大轮数,默认: 2"
+    )
+    parser.add_argument(
+        "--sug-threshold",
+        type=float,
+        default=0.7,
+        help="suggestion阈值,默认: 0.7"
+    )
+    parser.add_argument(
+        "--visualize",
+        action="store_true",
+        default=True,
+        help="运行完成后自动生成可视化HTML"
+    )
+    args = parser.parse_args()
+
+    asyncio.run(main(args.input_dir, max_rounds=args.max_rounds, sug_threshold=args.sug_threshold, visualize=args.visualize))

+ 1103 - 0
sug_v6_1_2_114.py

@@ -0,0 +1,1103 @@
+import asyncio
+import json
+import os
+import sys
+import argparse
+from datetime import datetime
+from typing import Literal
+
+from agents import Agent, Runner
+from lib.my_trace import set_trace
+from pydantic import BaseModel, Field
+
+from lib.utils import read_file_as_string
+from lib.client import get_model
+MODEL_NAME = "google/gemini-2.5-flash"
+from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
+from script.search.xiaohongshu_search import XiaohongshuSearch
+
+
+# ============================================================================
+# 数据模型
+# ============================================================================
+
+class Seg(BaseModel):
+    """分词"""
+    text: str
+    score_with_o: float = 0.0  # 与原始问题的评分
+    reason: str = ""  # 评分理由
+    from_o: str = ""  # 原始问题
+
+
+class Word(BaseModel):
+    """词"""
+    text: str
+    score_with_o: float = 0.0  # 与原始问题的评分
+    from_o: str = ""  # 原始问题
+
+
+class QFromQ(BaseModel):
+    """Q来源信息(用于Sug中记录)"""
+    text: str
+    score_with_o: float = 0.0
+
+
+class Q(BaseModel):
+    """查询"""
+    text: str
+    score_with_o: float = 0.0  # 与原始问题的评分
+    reason: str = ""  # 评分理由
+    from_source: str = ""  # seg/sug/add(加词)
+
+
+class Sug(BaseModel):
+    """建议词"""
+    text: str
+    score_with_o: float = 0.0  # 与原始问题的评分
+    reason: str = ""  # 评分理由
+    from_q: QFromQ | None = None  # 来自的q
+
+
+class Seed(BaseModel):
+    """种子"""
+    text: str
+    added_words: list[str] = Field(default_factory=list)  # 已经增加的words
+    from_type: str = ""  # seg/sug
+    score_with_o: float = 0.0  # 与原始问题的评分
+
+
+class Post(BaseModel):
+    """帖子"""
+    title: str = ""
+    body_text: str = ""
+    type: str = "normal"  # video/normal
+    images: list[str] = Field(default_factory=list)  # 图片url列表,第一张为封面
+    video: str = ""  # 视频url
+    interact_info: dict = Field(default_factory=dict)  # 互动信息
+    note_id: str = ""
+    note_url: str = ""
+
+
+class Search(Sug):
+    """搜索结果(继承Sug)"""
+    post_list: list[Post] = Field(default_factory=list)  # 搜索得到的帖子列表
+
+
+class RunContext(BaseModel):
+    """运行上下文"""
+    version: str
+    input_files: dict[str, str]
+    c: str  # 原始需求
+    o: str  # 原始问题
+    log_url: str
+    log_dir: str
+
+    # 每轮的数据
+    rounds: list[dict] = Field(default_factory=list)  # 每轮的详细数据
+
+    # 最终结果
+    final_output: str | None = None
+
+
+# ============================================================================
+# Agent 定义
+# ============================================================================
+
+# Agent 1: 分词专家
+class WordSegmentation(BaseModel):
+    """分词结果"""
+    words: list[str] = Field(..., description="分词结果列表")
+    reasoning: str = Field(..., description="分词理由")
+
+word_segmentation_instructions = """
+你是分词专家。给定一个query,将其拆分成有意义的最小单元。
+
+## 分词原则
+1. 保留有搜索意义的词汇
+2. 拆分成独立的概念
+3. 保留专业术语的完整性
+4. 去除虚词(的、吗、呢等)
+
+## 输出要求
+返回分词列表和分词理由。
+""".strip()
+
+word_segmenter = Agent[None](
+    name="分词专家",
+    instructions=word_segmentation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=WordSegmentation,
+)
+
+
+# Agent 2: 相关度评估专家
+class RelevanceEvaluation(BaseModel):
+    """相关度评估"""
+    reason: str = Field(..., description="评估理由")
+    relevance_score: float = Field(..., description="相关性分数 -1~1")
+
+relevance_evaluation_instructions = """
+# 角色定义
+你是一个 **专业的语言专家和语义相关性评判专家**。你的任务是:判断我给你的 <平台sug词条> 与 <原始问题> 的相关度满足度,给出 **-1 到 1 之间** 的数值评分。
+
+---
+
+# 核心概念与方法论
+
+## 两大评估维度
+本评估系统始终围绕 **两个核心维度** 进行:
+
+### 1. 动机维度(权重70%)
+**定义:** 用户"想要做什么",即原始问题的行为意图和目的
+- 核心是 **动词**:获取、学习、拍摄、制作、寻找等
+- 包括:核心动作 + 使用场景 + 最终目的
+
+### 2. 品类维度(权重30%)
+**定义:** 用户"关于什么内容",即原始问题的主题对象和限定词
+- 核心是 **名词+限定词**:川西秋季风光摄影素材
+- 包括:核心主体 + 地域限定 + 时间限定 + 质量限定等
+
+---
+
+## 如何识别原始问题的核心动机
+
+**核心动机必须是动词**,识别方法如下:
+
+### 方法1: 显性动词直接提取
+
+当原始问题明确包含动词时,直接提取
+示例:
+"如何获取素材" → 核心动机 = "获取"
+"寻找拍摄技巧" → 核心动机 = "寻找"(或"学习")
+"制作视频教程" → 核心动机 = "制作"
+
+### 方法2: 隐性动词语义推理
+
+当原始问题没有显性动词时,需要结合上下文推理
+示例:
+例: "川西秋天风光摄影" → 隐含动作="拍摄"
+→ 需结合上下文判断
+
+如果原始问题是纯名词短语,无任何动作线索:
+→ 核心动机 = 无法识别
+→ 初始权重 = 0
+→ 相关度评估以品类匹配为主
+示例:
+"摄影" → 无法识别动机,初始权重=0
+"川西风光" → 无法识别动机,初始权重=0
+
+
+
+# 输入信息
+你将接收到以下输入:
+- **<原始问题>**:用户的初始查询问题,代表用户的真实需求意图。
+- **<平台sug词条>**:平台推荐的词条列表,每个词条需要单独评估。
+
+
+#判定流程
+#评估架构
+
+输入: <原始问题> + <平台sug词条>
+         ↓
+【综合相关性判定】
+    ├→ 步骤1: 评估<sug词条>与<原始问题>的相关度
+    └→ 输出: -1到1之间的数值 + 分维度得分 + 判定依据
+
+
+相关度评估维度详解
+维度1: 动机维度评估(权重70%)
+评估对象: <平台sug词条> 与 <原始问题> 的需求动机匹配度
+说明: 核心动作是用户需求的第一优先级,决定了推荐的基本有效性
+
+
+评分标准:
+
+【正向匹配】
++1.0: 核心动作完全一致
+  - 例: 原始问题"如何获取素材" vs sug词"素材获取方法"
+  - 特殊规则: 如果sug词的核心动作是原始问题动作的**具体化子集**,也判定为完全一致
+    · 例: 原始问题"扣除猫咪主体的方法" vs sug词"扣除猫咪眼睛的方法"(子集但目的一致)
+
++0.8~0.95: 核心动作语义相近或为同义表达
+  - 例: 原始问题"如何获取素材" vs sug词"素材下载教程"
+  - 同义词对: 获取≈下载≈寻找, 技巧≈方法≈教程≈攻略
+
++0.5~0.75: 核心动作相关但非直接对应(相关实现路径)
+  - 例: 原始问题"如何获取素材" vs sug词"素材管理整理"
+
++0.2~0.45: 核心动作弱相关(同领域不同动作)
+  - 例: 原始问题"如何拍摄风光" vs sug词"风光摄影欣赏"
+
+【中性/无关】
+0: 没有明确目的,动作意图无明确关联
+  - 例: 原始问题"如何获取素材" vs sug词"摄影器材推荐"
+  - 例: 原始问题无法识别动机 且 sug词也无明确动作 → 0
+
+【负向偏离】
+-0.2~-0.05: 动作意图轻度冲突或误导
+  - 例: 原始问题"如何获取素材" vs sug词"素材版权保护须知"
+
+-0.5~-0.25: 动作意图明显对立
+  - 例: 原始问题"如何获取免费素材" vs sug词"如何售卖素材"
+
+-1.0~-0.55: 动作意图完全相反或产生严重负面引导
+  - 例: 原始问题"免费素材获取" vs sug词"付费素材强制推销"
+
+维度2: 品类维度评估(权重30%)
+评估对象: <平台sug词条> 与 <原始问题> 的内容主体和限定词匹配度
+
+评分标准:
+
+【正向匹配】
++1.0: 核心主体+所有关键限定词完全匹配
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"川西秋季风光摄影作品"
+
++0.75~0.95: 核心主体匹配,大部分限定词匹配
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"川西风光摄影素材"(缺失"秋季")
+
++0.5~0.7: 核心主体匹配,少量限定词匹配或合理泛化
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"四川风光摄影"
+
++0.2~0.45: 仅主体词匹配,限定词全部缺失或错位
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"风光摄影入门"
+
++0.05~0.15: 主题领域相关但品类不同
+  - 例: 原始问题"风光摄影素材" vs sug词"人文摄影素材"
+
+【中性/无关】
+0: 主体词部分相关但类别明显不同
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"人像摄影素材"
+
+【负向偏离】
+-0.2~-0.05: 主体词或限定词存在误导性
+  - 例: 原始问题"免费摄影素材" vs sug词"付费摄影素材库"
+
+-0.5~-0.25: 主体词明显错位或品类冲突
+  - 例: 原始问题"风光摄影素材" vs sug词"人像修图教程"
+
+-1.0~-0.55: 完全错误的品类或有害引导
+  - 例: 原始问题"正版素材获取" vs sug词"盗版素材下载"
+
+
+综合得分计算与规则调整
+步骤1: 应用依存性规则
+规则A: 动机高分保护机制
+
+如果 动机维度得分 ≥ 0.8:
+   → 品类得分即使为0或轻微负向(-0.2~0)
+   → 最终得分 = max(初步得分, 0.55)
+
+解释: 当目的高度一致时,品类的泛化不应导致"弱相关"
+
+规则B: 动机低分限制机制
+如果 动机维度得分 ≤ 0.2:
+   → 无论品类得分多高
+   → 最终得分 = min(初步得分, 0.4)
+
+解释: 目的不符时,品类匹配的价值有限
+
+规则C: 动机负向决定机制
+如果 动机维度得分 < 0:
+   → 最终得分 = min(初步得分, 0)
+
+解释: 动作意图冲突时,推荐具有误导性,不应为正相关
+
+步骤3: 输出最终得分
+
+#基础加权计算
+应用规则后的调整得分 = 目的动机维度得分 × 0.7 + 品类维度得分 × 0.3
+取值范围: -1.0 ~ +1.0
+
+---
+
+# 得分档位解释
+
+高度相关】+0.8 ~ +1.0
+相关性高度契合,用户可直接使用
+动机和品类均高度匹配
+典型场景: 动机≥0.85 且 品类≥0.7
+【中高相关】+0.6 ~ +0.79
+相关性较好,用户基本满意
+动机匹配但品类有泛化,或反之
+典型场景: 动机≥0.8 且 品类≥0.3
+【中度相关】+0.3 ~ +0.59
+部分相关,用户需要调整搜索策略
+动机或品类存在一定偏差
+典型场景: 动机0.4-0.7 且 品类0.3-0.7
+【弱相关】+0.01 ~ +0.29
+关联微弱,参考价值有限
+仅有表层词汇重叠
+【无关】0
+无明确关联
+原始问题无法识别动机 且 sug词无明确动作
+没有目的性且没有品类匹配
+【轻度负向】-0.29 ~ -0.01
+产生轻微误导或干扰
+【中度负向】-0.69 ~ -0.3
+存在明显冲突或误导
+【严重负向】-1.0 ~ -0.7
+完全违背意图或产生有害引导
+
+---
+
+# 输出要求
+输出结果必须为一个 **JSON 格式**,包含以下内容:
+
+#注意事项:
+始终围绕两个核心维度:所有评估都基于"动机"和"品类"两个维度,不偏离
+核心动机必须是动词:在评估前,必须先提取原始问题的核心动机(动词),这是整个评估的基础
+严格标准一致性:对所有用例使用相同的评估标准,避免评分飘移
+负分使用原则:仅当sug词条对原始问题产生误导、冲突或有害引导时给予负分
+零分使用原则:当sug词条与原始问题无明确关联,既不相关也不冲突时给予零分
+分维度独立评分:
+先提取原始问题核心动机
+分别计算动机维度(含两个子维度)和品类维度得分
+按70:30加权得到初步得分
+应用规则调整得到最终得分
+动机优先原则:当动机高度一致时,品类的合理泛化或具体化不应导致低评分
+技巧类需求特殊对待:包含"技巧/方法/教程"等词的需求,对动作一致性要求更严格
+
+## 输出格式(严格遵守)
+
+必须输出标准 JSON 格式,包含以下两个字段:
+
+```json
+{
+  "reason": "详细的评估理由说明",
+  "relevance_score": 0.85
+}
+```
+
+### 字段说明
+- **reason** (string): 详细的评估理由,说明动机维度和品类维度的匹配情况
+- **relevance_score** (number): -1.0 到 1.0 之间的数值,保留2位小数
+
+### 重要约束
+1. **reason 字段内容规则**:
+   - 禁止使用英文双引号 `"`,必须使用替代符号
+   - 推荐使用中文书名号:《》或【】
+   - 或使用中文引号:「」或『』
+   - 示例:使用"核心动机是《制作攻略图》"而不是"核心动机是"制作攻略图""
+
+2. **JSON 格式要求**:
+   - 必须是合法的 JSON 格式
+   - relevance_score 必须是数字类型,不能是字符串
+   - 字段名必须用双引号包裹
+
+### 输出示例
+
+✅ **正确示例**:
+```json
+{
+  "reason": "原始问题的核心动机是《制作攻略图》,包含【拼接】图片等操作。sug词条【拼接】与原始问题中的具体操作完全一致,动机维度匹配度为中等。品类维度相关性较弱。",
+  "relevance_score": 0.22
+}
+```
+
+❌ **错误示例**(会导致解析失败):
+```json
+{
+  "reason": "原始问题的核心动机是"制作攻略图",包含"拼接"图片",
+  "relevance_score": "0.22"
+}
+```
+错误原因:reason 中使用了未转义的英文双引号,relevance_score 是字符串而非数字
+""".strip()
+
+relevance_evaluator = Agent[None](
+    name="相关度评估专家",
+    instructions=relevance_evaluation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=RelevanceEvaluation,
+)
+
+
+# Agent 3: 加词选择专家
+class WordSelection(BaseModel):
+    """加词选择结果"""
+    selected_word: str = Field(..., description="选择的词")
+    combined_query: str = Field(..., description="组合后的新query")
+    reasoning: str = Field(..., description="选择理由")
+
+word_selection_instructions = """
+你是加词选择专家。
+
+## 任务
+从候选词列表中选择一个最合适的词,与当前seed组合成新的query。
+
+## 原则
+1. 选择与当前seed最相关的词
+2. 组合后的query要语义通顺
+3. 符合搜索习惯
+4. 优先选择能扩展搜索范围的词
+
+## 输出
+- selected_word: 选中的词
+- combined_query: 组合后的新query
+- reasoning: 选择理由
+""".strip()
+
+word_selector = Agent[None](
+    name="加词选择专家",
+    instructions=word_selection_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=WordSelection,
+)
+
+
+# ============================================================================
+# 辅助函数
+# ============================================================================
+
+def process_note_data(note: dict) -> Post:
+    """处理搜索接口返回的帖子数据"""
+    note_card = note.get("note_card", {})
+    image_list = note_card.get("image_list", [])
+    interact_info = note_card.get("interact_info", {})
+    user_info = note_card.get("user", {})
+
+    # 提取图片URL - 使用新的字段名 image_url
+    images = []
+    for img in image_list:
+        if isinstance(img, dict):
+            # 尝试新字段名 image_url,如果不存在则尝试旧字段名 url_default
+            img_url = img.get("image_url") or img.get("url_default")
+            if img_url:
+                images.append(img_url)
+
+    # 判断类型
+    note_type = note_card.get("type", "normal")
+    video_url = ""
+    if note_type == "video":
+        video_info = note_card.get("video", {})
+        if isinstance(video_info, dict):
+            # 尝试获取视频URL
+            video_url = video_info.get("media", {}).get("stream", {}).get("h264", [{}])[0].get("master_url", "")
+
+    return Post(
+        note_id=note.get("id", ""),
+        title=note_card.get("display_title", ""),
+        body_text=note_card.get("desc", ""),
+        type=note_type,
+        images=images,
+        video=video_url,
+        interact_info={
+            "liked_count": interact_info.get("liked_count", 0),
+            "collected_count": interact_info.get("collected_count", 0),
+            "comment_count": interact_info.get("comment_count", 0),
+            "shared_count": interact_info.get("shared_count", 0)
+        },
+        note_url=f"https://www.xiaohongshu.com/explore/{note.get('id', '')}"
+    )
+
+
+async def evaluate_with_o(text: str, o: str) -> tuple[float, str]:
+    """评估文本与原始问题o的相关度
+
+    Returns:
+        tuple[float, str]: (相关度分数, 评估理由)
+    """
+    eval_input = f"""
+<原始问题>
+{o}
+</原始问题>
+
+<当前文本>
+{text}
+</当前文本>
+
+请评估当前文本与原始问题的相关度。
+"""
+    result = await Runner.run(relevance_evaluator, eval_input)
+    evaluation: RelevanceEvaluation = result.final_output
+    return evaluation.relevance_score, evaluation.reason
+
+
+# ============================================================================
+# 核心流程函数
+# ============================================================================
+
+async def initialize(o: str, context: RunContext) -> tuple[list[Seg], list[Word], list[Q], list[Seed]]:
+    """
+    初始化阶段
+
+    Returns:
+        (seg_list, word_list_1, q_list_1, seed_list)
+    """
+    print(f"\n{'='*60}")
+    print(f"初始化阶段")
+    print(f"{'='*60}")
+
+    # 1. 分词:原始问题(o) ->分词-> seg_list
+    print(f"\n[步骤1] 分词...")
+    result = await Runner.run(word_segmenter, o)
+    segmentation: WordSegmentation = result.final_output
+
+    seg_list = []
+    for word in segmentation.words:
+        seg_list.append(Seg(text=word, from_o=o))
+
+    print(f"分词结果: {[s.text for s in seg_list]}")
+    print(f"分词理由: {segmentation.reasoning}")
+
+    # 2. 分词评估:seg_list -> 每个seg与o进行评分(并发)
+    print(f"\n[步骤2] 评估每个分词与原始问题的相关度...")
+
+    async def evaluate_seg(seg: Seg) -> Seg:
+        seg.score_with_o, seg.reason = await evaluate_with_o(seg.text, o)
+        return seg
+
+    if seg_list:
+        eval_tasks = [evaluate_seg(seg) for seg in seg_list]
+        await asyncio.gather(*eval_tasks)
+
+    for seg in seg_list:
+        print(f"  {seg.text}: {seg.score_with_o:.2f}")
+
+    # 3. 构建word_list_1: seg_list -> word_list_1
+    print(f"\n[步骤3] 构建word_list_1...")
+    word_list_1 = []
+    for seg in seg_list:
+        word_list_1.append(Word(
+            text=seg.text,
+            score_with_o=seg.score_with_o,
+            from_o=o
+        ))
+    print(f"word_list_1: {[w.text for w in word_list_1]}")
+
+    # 4. 构建q_list_1:seg_list 作为 q_list_1
+    print(f"\n[步骤4] 构建q_list_1...")
+    q_list_1 = []
+    for seg in seg_list:
+        q_list_1.append(Q(
+            text=seg.text,
+            score_with_o=seg.score_with_o,
+            reason=seg.reason,
+            from_source="seg"
+        ))
+    print(f"q_list_1: {[q.text for q in q_list_1]}")
+
+    # 5. 构建seed_list: seg_list -> seed_list
+    print(f"\n[步骤5] 构建seed_list...")
+    seed_list = []
+    for seg in seg_list:
+        seed_list.append(Seed(
+            text=seg.text,
+            added_words=[],
+            from_type="seg",
+            score_with_o=seg.score_with_o
+        ))
+    print(f"seed_list: {[s.text for s in seed_list]}")
+
+    return seg_list, word_list_1, q_list_1, seed_list
+
+
+async def run_round(
+    round_num: int,
+    q_list: list[Q],
+    word_list: list[Word],
+    seed_list: list[Seed],
+    o: str,
+    context: RunContext,
+    xiaohongshu_api: XiaohongshuSearchRecommendations,
+    xiaohongshu_search: XiaohongshuSearch,
+    sug_threshold: float = 0.7
+) -> tuple[list[Word], list[Q], list[Seed], list[Search]]:
+    """
+    运行一轮
+
+    Args:
+        round_num: 轮次编号
+        q_list: 当前轮的q列表
+        word_list: 当前的word列表
+        seed_list: 当前的seed列表
+        o: 原始问题
+        context: 运行上下文
+        xiaohongshu_api: 建议词API
+        xiaohongshu_search: 搜索API
+        sug_threshold: suggestion的阈值
+
+    Returns:
+        (word_list_next, q_list_next, seed_list_next, search_list)
+    """
+    print(f"\n{'='*60}")
+    print(f"第{round_num}轮")
+    print(f"{'='*60}")
+
+    round_data = {
+        "round_num": round_num,
+        "input_q_list": [{"text": q.text, "score": q.score_with_o} for q in q_list],
+        "input_word_list_size": len(word_list),
+        "input_seed_list_size": len(seed_list)
+    }
+
+    # 1. 请求sug:q_list -> 每个q请求sug接口 -> sug_list_list
+    print(f"\n[步骤1] 为每个q请求建议词...")
+    sug_list_list = []  # list of list
+    for q in q_list:
+        print(f"\n  处理q: {q.text}")
+        suggestions = xiaohongshu_api.get_recommendations(keyword=q.text)
+
+        q_sug_list = []
+        if suggestions:
+            print(f"    获取到 {len(suggestions)} 个建议词")
+            for sug_text in suggestions:
+                sug = Sug(
+                    text=sug_text,
+                    from_q=QFromQ(text=q.text, score_with_o=q.score_with_o)
+                )
+                q_sug_list.append(sug)
+        else:
+            print(f"    未获取到建议词")
+
+        sug_list_list.append(q_sug_list)
+
+    # 2. sug评估:sug_list_list -> 每个sug与o进评分(并发)
+    print(f"\n[步骤2] 评估每个建议词与原始问题的相关度...")
+
+    # 2.1 收集所有需要评估的sug,并记录它们所属的q
+    all_sugs = []
+    sug_to_q_map = {}  # 记录每个sug属于哪个q
+    for i, q_sug_list in enumerate(sug_list_list):
+        if q_sug_list:
+            q_text = q_list[i].text
+            for sug in q_sug_list:
+                all_sugs.append(sug)
+                sug_to_q_map[id(sug)] = q_text
+
+    # 2.2 并发评估所有sug
+    async def evaluate_sug(sug: Sug) -> Sug:
+        sug.score_with_o, sug.reason = await evaluate_with_o(sug.text, o)
+        return sug
+
+    if all_sugs:
+        eval_tasks = [evaluate_sug(sug) for sug in all_sugs]
+        await asyncio.gather(*eval_tasks)
+
+    # 2.3 打印结果并组织到sug_details
+    sug_details = {}  # 保存每个Q对应的sug列表
+    for i, q_sug_list in enumerate(sug_list_list):
+        if q_sug_list:
+            q_text = q_list[i].text
+            print(f"\n  来自q '{q_text}' 的建议词:")
+            sug_details[q_text] = []
+            for sug in q_sug_list:
+                print(f"    {sug.text}: {sug.score_with_o:.2f}")
+                # 保存到sug_details
+                sug_details[q_text].append({
+                    "text": sug.text,
+                    "score": sug.score_with_o,
+                    "reason": sug.reason
+                })
+
+    # 3. search_list构建
+    print(f"\n[步骤3] 构建search_list(阈值>{sug_threshold})...")
+    search_list = []
+    high_score_sugs = [sug for sug in all_sugs if sug.score_with_o > sug_threshold]
+
+    if high_score_sugs:
+        print(f"  找到 {len(high_score_sugs)} 个高分建议词")
+
+        # 并发搜索
+        async def search_for_sug(sug: Sug) -> Search:
+            print(f"    搜索: {sug.text}")
+            try:
+                search_result = xiaohongshu_search.search(keyword=sug.text)
+                result_str = search_result.get("result", "{}")
+                if isinstance(result_str, str):
+                    result_data = json.loads(result_str)
+                else:
+                    result_data = result_str
+
+                notes = result_data.get("data", {}).get("data", [])
+                post_list = []
+                for note in notes[:10]:  # 只取前10个
+                    post = process_note_data(note)
+                    post_list.append(post)
+
+                print(f"      → 找到 {len(post_list)} 个帖子")
+
+                return Search(
+                    text=sug.text,
+                    score_with_o=sug.score_with_o,
+                    from_q=sug.from_q,
+                    post_list=post_list
+                )
+            except Exception as e:
+                print(f"      ✗ 搜索失败: {e}")
+                return Search(
+                    text=sug.text,
+                    score_with_o=sug.score_with_o,
+                    from_q=sug.from_q,
+                    post_list=[]
+                )
+
+        search_tasks = [search_for_sug(sug) for sug in high_score_sugs]
+        search_list = await asyncio.gather(*search_tasks)
+    else:
+        print(f"  没有高分建议词,search_list为空")
+
+    # 4. 构建word_list_next: word_list -> word_list_next(先直接复制)
+    print(f"\n[步骤4] 构建word_list_next(暂时直接复制)...")
+    word_list_next = word_list.copy()
+
+    # 5. 构建q_list_next
+    print(f"\n[步骤5] 构建q_list_next...")
+    q_list_next = []
+    add_word_details = {}  # 保存每个seed对应的组合词列表
+
+    # 5.1 对于seed_list中的每个seed,从word_list_next中选一个未加过的词
+    print(f"\n  5.1 为每个seed加词...")
+    for seed in seed_list:
+        print(f"\n    处理seed: {seed.text}")
+
+        # 简单过滤:找出不在seed.text中且未被添加过的词
+        candidate_words = []
+        for word in word_list_next:
+            # 检查词是否已在seed中
+            if word.text in seed.text:
+                continue
+            # 检查词是否已被添加过
+            if word.text in seed.added_words:
+                continue
+            candidate_words.append(word)
+
+        if not candidate_words:
+            print(f"      没有可用的候选词")
+            continue
+
+        print(f"      候选词: {[w.text for w in candidate_words]}")
+
+        # 使用Agent选择最合适的词
+        selection_input = f"""
+<原始问题>
+{o}
+</原始问题>
+
+<当前Seed>
+{seed.text}
+</当前Seed>
+
+<候选词列表>
+{', '.join([w.text for w in candidate_words])}
+</候选词列表>
+
+请从候选词中选择一个最合适的词,与当前seed组合成新的query。
+"""
+        result = await Runner.run(word_selector, selection_input)
+        selection: WordSelection = result.final_output
+
+        # 验证选择的词是否在候选列表中
+        if selection.selected_word not in [w.text for w in candidate_words]:
+            print(f"      ✗ Agent选择的词 '{selection.selected_word}' 不在候选列表中,跳过")
+            continue
+
+        print(f"      ✓ 选择词: {selection.selected_word}")
+        print(f"      ✓ 新query: {selection.combined_query}")
+        print(f"      理由: {selection.reasoning}")
+
+        # 评估新query
+        new_q_score, new_q_reason = await evaluate_with_o(selection.combined_query, o)
+        print(f"      新query评分: {new_q_score:.2f}")
+
+        # 创建新的q
+        new_q = Q(
+            text=selection.combined_query,
+            score_with_o=new_q_score,
+            reason=new_q_reason,
+            from_source="add"
+        )
+        q_list_next.append(new_q)
+
+        # 更新seed的added_words
+        seed.added_words.append(selection.selected_word)
+
+        # 保存到add_word_details
+        if seed.text not in add_word_details:
+            add_word_details[seed.text] = []
+        add_word_details[seed.text].append({
+            "text": selection.combined_query,
+            "score": new_q_score,
+            "reason": new_q_reason,
+            "selected_word": selection.selected_word
+        })
+
+    # 5.2 对于sug_list_list中,每个sug大于来自的query分数,加到q_list_next
+    print(f"\n  5.2 将高分sug加入q_list_next...")
+    for sug in all_sugs:
+        if sug.from_q and sug.score_with_o > sug.from_q.score_with_o:
+            new_q = Q(
+                text=sug.text,
+                score_with_o=sug.score_with_o,
+                reason=sug.reason,
+                from_source="sug"
+            )
+            q_list_next.append(new_q)
+            print(f"    ✓ {sug.text} (分数: {sug.score_with_o:.2f} > {sug.from_q.score_with_o:.2f})")
+
+    # 6. 更新seed_list
+    print(f"\n[步骤6] 更新seed_list...")
+    seed_list_next = seed_list.copy()  # 保留原有的seed
+
+    # 对于sug_list_list中,每个sug分数大于来源query分数的,且没在seed_list中出现过的,加入
+    existing_seed_texts = {seed.text for seed in seed_list_next}
+    for sug in all_sugs:
+        # 新逻辑:sug分数 > 对应query分数
+        if sug.from_q and sug.score_with_o > sug.from_q.score_with_o and sug.text not in existing_seed_texts:
+            new_seed = Seed(
+                text=sug.text,
+                added_words=[],
+                from_type="sug",
+                score_with_o=sug.score_with_o
+            )
+            seed_list_next.append(new_seed)
+            existing_seed_texts.add(sug.text)
+            print(f"  ✓ 新seed: {sug.text} (分数: {sug.score_with_o:.2f} > 来源query: {sug.from_q.score_with_o:.2f})")
+
+    # 序列化搜索结果数据(包含帖子详情)
+    search_results_data = []
+    for search in search_list:
+        search_results_data.append({
+            "text": search.text,
+            "score_with_o": search.score_with_o,
+            "post_list": [
+                {
+                    "note_id": post.note_id,
+                    "note_url": post.note_url,
+                    "title": post.title,
+                    "body_text": post.body_text,
+                    "images": post.images,
+                    "interact_info": post.interact_info
+                }
+                for post in search.post_list
+            ]
+        })
+
+    # 记录本轮数据
+    round_data.update({
+        "sug_count": len(all_sugs),
+        "high_score_sug_count": len(high_score_sugs),
+        "search_count": len(search_list),
+        "total_posts": sum(len(s.post_list) for s in search_list),
+        "q_list_next_size": len(q_list_next),
+        "seed_list_next_size": len(seed_list_next),
+        "word_list_next_size": len(word_list_next),
+        "output_q_list": [{"text": q.text, "score": q.score_with_o, "reason": q.reason, "from": q.from_source} for q in q_list_next],
+        "seed_list_next": [{"text": seed.text, "from": seed.from_type, "score": seed.score_with_o} for seed in seed_list_next],  # 下一轮种子列表
+        "sug_details": sug_details,  # 每个Q对应的sug列表
+        "add_word_details": add_word_details,  # 每个seed对应的组合词列表
+        "search_results": search_results_data  # 搜索结果(包含帖子详情)
+    })
+    context.rounds.append(round_data)
+
+    print(f"\n本轮总结:")
+    print(f"  建议词数量: {len(all_sugs)}")
+    print(f"  高分建议词: {len(high_score_sugs)}")
+    print(f"  搜索数量: {len(search_list)}")
+    print(f"  帖子总数: {sum(len(s.post_list) for s in search_list)}")
+    print(f"  下轮q数量: {len(q_list_next)}")
+    print(f"  seed数量: {len(seed_list_next)}")
+
+    return word_list_next, q_list_next, seed_list_next, search_list
+
+
+async def iterative_loop(
+    context: RunContext,
+    max_rounds: int = 2,
+    sug_threshold: float = 0.7
+):
+    """主迭代循环"""
+
+    print(f"\n{'='*60}")
+    print(f"开始迭代循环")
+    print(f"最大轮数: {max_rounds}")
+    print(f"sug阈值: {sug_threshold}")
+    print(f"{'='*60}")
+
+    # 初始化
+    seg_list, word_list, q_list, seed_list = await initialize(context.o, context)
+
+    # API实例
+    xiaohongshu_api = XiaohongshuSearchRecommendations()
+    xiaohongshu_search = XiaohongshuSearch()
+
+    # 保存初始化数据
+    context.rounds.append({
+        "round_num": 0,
+        "type": "initialization",
+        "seg_list": [{"text": s.text, "score": s.score_with_o, "reason": s.reason} for s in seg_list],
+        "word_list_1": [{"text": w.text, "score": w.score_with_o} for w in word_list],
+        "q_list_1": [{"text": q.text, "score": q.score_with_o, "reason": q.reason} for q in q_list],
+        "seed_list": [{"text": s.text, "from_type": s.from_type, "score": s.score_with_o} for s in seed_list]
+    })
+
+    # 收集所有搜索结果
+    all_search_list = []
+
+    # 迭代
+    round_num = 1
+    while q_list and round_num <= max_rounds:
+        word_list, q_list, seed_list, search_list = await run_round(
+            round_num=round_num,
+            q_list=q_list,
+            word_list=word_list,
+            seed_list=seed_list,
+            o=context.o,
+            context=context,
+            xiaohongshu_api=xiaohongshu_api,
+            xiaohongshu_search=xiaohongshu_search,
+            sug_threshold=sug_threshold
+        )
+
+        all_search_list.extend(search_list)
+        round_num += 1
+
+    print(f"\n{'='*60}")
+    print(f"迭代完成")
+    print(f"  总轮数: {round_num - 1}")
+    print(f"  总搜索次数: {len(all_search_list)}")
+    print(f"  总帖子数: {sum(len(s.post_list) for s in all_search_list)}")
+    print(f"{'='*60}")
+
+    return all_search_list
+
+
+# ============================================================================
+# 主函数
+# ============================================================================
+
+async def main(input_dir: str, max_rounds: int = 2, sug_threshold: float = 0.7, visualize: bool = False):
+    """主函数"""
+    current_time, log_url = set_trace()
+
+    # 读取输入
+    input_context_file = os.path.join(input_dir, 'context.md')
+    input_q_file = os.path.join(input_dir, 'q.md')
+
+    c = read_file_as_string(input_context_file)  # 原始需求
+    o = read_file_as_string(input_q_file)  # 原始问题
+
+    # 版本信息
+    version = os.path.basename(__file__)
+    version_name = os.path.splitext(version)[0]
+
+    # 日志目录
+    log_dir = os.path.join(input_dir, "output", version_name, current_time)
+
+    # 创建运行上下文
+    run_context = RunContext(
+        version=version,
+        input_files={
+            "input_dir": input_dir,
+            "context_file": input_context_file,
+            "q_file": input_q_file,
+        },
+        c=c,
+        o=o,
+        log_dir=log_dir,
+        log_url=log_url,
+    )
+
+    # 执行迭代
+    all_search_list = await iterative_loop(
+        run_context,
+        max_rounds=max_rounds,
+        sug_threshold=sug_threshold
+    )
+
+    # 格式化输出
+    output = f"原始需求:{run_context.c}\n"
+    output += f"原始问题:{run_context.o}\n"
+    output += f"总搜索次数:{len(all_search_list)}\n"
+    output += f"总帖子数:{sum(len(s.post_list) for s in all_search_list)}\n"
+    output += "\n" + "="*60 + "\n"
+
+    if all_search_list:
+        output += "【搜索结果】\n\n"
+        for idx, search in enumerate(all_search_list, 1):
+            output += f"{idx}. 搜索词: {search.text} (分数: {search.score_with_o:.2f})\n"
+            output += f"   帖子数: {len(search.post_list)}\n"
+            if search.post_list:
+                for post_idx, post in enumerate(search.post_list[:3], 1):  # 只显示前3个
+                    output += f"   {post_idx}) {post.title}\n"
+                    output += f"      URL: {post.note_url}\n"
+            output += "\n"
+    else:
+        output += "未找到搜索结果\n"
+
+    run_context.final_output = output
+
+    print(f"\n{'='*60}")
+    print("最终结果")
+    print(f"{'='*60}")
+    print(output)
+
+    # 保存日志
+    os.makedirs(run_context.log_dir, exist_ok=True)
+
+    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
+    context_dict = run_context.model_dump()
+    with open(context_file_path, "w", encoding="utf-8") as f:
+        json.dump(context_dict, f, ensure_ascii=False, indent=2)
+    print(f"\nRunContext saved to: {context_file_path}")
+
+    # 保存详细的搜索结果
+    search_results_path = os.path.join(run_context.log_dir, "search_results.json")
+    search_results_data = [s.model_dump() for s in all_search_list]
+    with open(search_results_path, "w", encoding="utf-8") as f:
+        json.dump(search_results_data, f, ensure_ascii=False, indent=2)
+    print(f"Search results saved to: {search_results_path}")
+
+    # 可视化
+    if visualize:
+        import subprocess
+        output_html = os.path.join(run_context.log_dir, "visualization.html")
+        print(f"\n🎨 生成可视化HTML...")
+
+        # 获取绝对路径
+        abs_context_file = os.path.abspath(context_file_path)
+        abs_output_html = os.path.abspath(output_html)
+
+        # 运行可视化脚本
+        result = subprocess.run([
+            "node",
+            "visualization/sug_v6_1_2_8/index.js",
+            abs_context_file,
+            abs_output_html
+        ])
+
+        if result.returncode == 0:
+            print(f"✅ 可视化已生成: {output_html}")
+        else:
+            print(f"❌ 可视化生成失败")
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description="搜索query优化工具 - v6.1.2.8 轮次迭代版")
+    parser.add_argument(
+        "--input-dir",
+        type=str,
+        default="input/旅游-逸趣玩旅行/如何获取能体现川西秋季特色的高质量风光摄影素材?",
+        help="输入目录路径,默认: input/旅游-逸趣玩旅行/如何获取能体现川西秋季特色的高质量风光摄影素材?"
+    )
+    parser.add_argument(
+        "--max-rounds",
+        type=int,
+        default=4,
+        help="最大轮数,默认: 2"
+    )
+    parser.add_argument(
+        "--sug-threshold",
+        type=float,
+        default=0.7,
+        help="suggestion阈值,默认: 0.7"
+    )
+    parser.add_argument(
+        "--visualize",
+        action="store_true",
+        default=True,
+        help="运行完成后自动生成可视化HTML"
+    )
+    args = parser.parse_args()
+
+    asyncio.run(main(args.input_dir, max_rounds=args.max_rounds, sug_threshold=args.sug_threshold, visualize=args.visualize))

+ 1 - 1
sug_v6_1_2_117.py

@@ -1308,7 +1308,7 @@ async def iterative_loop(
 # 主函数
 # ============================================================================
 
-async def main(input_dir: str, max_rounds: int = 2, sug_threshold: float = 0.7, visualize: bool = False):
+async def main(input_dir: str, max_rounds: int = 2, sug_threshold: float = 0.5, visualize: bool = False):
     """主函数"""
     current_time, log_url = set_trace()
 

+ 44 - 0
sug_v6_1_2_118.py

@@ -212,6 +212,7 @@ motivation_evaluation_instructions = """
 例: "川西秋天风光摄影" → 隐含动作="拍摄"
 → 需结合上下文判断
 
+
 如果原始问题是纯名词短语,无任何动作线索:
 → 核心动机 = 无法识别
 → 在此情况下,动机维度得分应为 0。
@@ -932,6 +933,33 @@ async def run_round(
                     "type": "sug"
                 })
 
+    # 2.4 剪枝判断(第2轮及以后生效)
+    pruned_query_texts = set()
+    if round_num >= 2:
+        print(f"\n[剪枝判断] 第{round_num}轮开始应用剪枝策略...")
+        for i, q in enumerate(q_list):
+            q_sug_list = sug_list_list[i]
+
+            if len(q_sug_list) == 0:
+                continue  # 没有sug则不剪枝
+
+            # 剪枝条件1: 所有sug分数都低于query分数
+            all_lower_than_query = all(sug.score_with_o < q.score_with_o for sug in q_sug_list)
+            # 剪枝条件2: 所有sug分数都低于0.5
+            all_below_threshold = all(sug.score_with_o < 0.5 for sug in q_sug_list)
+
+            if all_lower_than_query and all_below_threshold:
+                pruned_query_texts.add(q.text)
+                max_sug_score = max(sug.score_with_o for sug in q_sug_list)
+                print(f"  🔪 剪枝: {q.text} (query分数:{q.score_with_o:.2f}, sug最高分:{max_sug_score:.2f}, 全部<0.5)")
+
+        if pruned_query_texts:
+            print(f"  本轮共剪枝 {len(pruned_query_texts)} 个query")
+        else:
+            print(f"  本轮无query被剪枝")
+    else:
+        print(f"\n[剪枝判断] 第{round_num}轮不应用剪枝策略")
+
     # 3. search_list构建
     print(f"\n[步骤3] 构建search_list(阈值>{sug_threshold})...")
     search_list = []
@@ -991,6 +1019,11 @@ async def run_round(
     for seed in seed_list:
         print(f"\n    处理seed: {seed.text}")
 
+        # 剪枝检查:跳过被剪枝的seed
+        if seed.text in pruned_query_texts:
+            print(f"      ⊗ 跳过被剪枝的seed: {seed.text}")
+            continue
+
         # 从固定词库word_list_1筛选候选词
         candidate_words = []
         for word in word_list_1:
@@ -1139,6 +1172,11 @@ async def run_round(
     # 4.2 对于sug_list_list中,每个sug大于来自的query分数,加到q_list_next(去重检查)
     print(f"\n  4.2 将高分sug加入q_list_next...")
     for sug in all_sugs:
+        # 剪枝检查:跳过来自被剪枝query的sug
+        if sug.from_q and sug.from_q.text in pruned_query_texts:
+            print(f"    ⊗ 跳过来自被剪枝query的sug: {sug.text} (来源: {sug.from_q.text})")
+            continue
+
         if sug.from_q and sug.score_with_o > sug.from_q.score_with_o:
             # 去重检查
             if sug.text in existing_q_texts:
@@ -1183,6 +1221,10 @@ async def run_round(
     # 5.2 加入高分sug
     print(f"  5.2 加入高分sug...")
     for sug in all_sugs:
+        # 剪枝检查:跳过来自被剪枝query的sug
+        if sug.from_q and sug.from_q.text in pruned_query_texts:
+            continue
+
         # sug分数 > 对应query分数
         if sug.from_q and sug.score_with_o > sug.from_q.score_with_o and sug.text not in existing_seed_texts:
             new_seed = Seed(
@@ -1223,6 +1265,8 @@ async def run_round(
         "q_list_next_size": len(q_list_next),
         "seed_list_next_size": len(seed_list_next),
         "total_combinations": len(all_seed_combinations),
+        "pruned_query_count": len(pruned_query_texts),
+        "pruned_queries": list(pruned_query_texts),
         "output_q_list": [{"text": q.text, "score": q.score_with_o, "reason": q.reason, "from": q.from_source, "type": "query"} for q in q_list_next],
         "seed_list_next": [{"text": seed.text, "from": seed.from_type, "score": seed.score_with_o} for seed in seed_list_next],
         "sug_details": sug_details,

+ 1690 - 0
sug_v6_1_2_120.py

@@ -0,0 +1,1690 @@
+import asyncio
+import json
+import os
+import sys
+import argparse
+from datetime import datetime
+from typing import Literal
+
+from agents import Agent, Runner, ModelSettings
+from lib.my_trace import set_trace
+from pydantic import BaseModel, Field
+
+from lib.utils import read_file_as_string
+from lib.client import get_model
+MODEL_NAME = "google/gemini-2.5-flash"
+# 得分提升阈值:sug或组合词必须比来源query提升至少此幅度才能进入下一轮
+REQUIRED_SCORE_GAIN = 0.02
+from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
+from script.search.xiaohongshu_search import XiaohongshuSearch
+
+
+# ============================================================================
+# 日志工具类
+# ============================================================================
+
+class TeeLogger:
+    """同时输出到控制台和日志文件的工具类"""
+    def __init__(self, stdout, log_file):
+        self.stdout = stdout
+        self.log_file = log_file
+
+    def write(self, message):
+        self.stdout.write(message)
+        self.log_file.write(message)
+        self.log_file.flush()  # 实时写入,避免丢失日志
+
+    def flush(self):
+        self.stdout.flush()
+        self.log_file.flush()
+
+
+# ============================================================================
+# 数据模型
+# ============================================================================
+
+class Seg(BaseModel):
+    """分词"""
+    text: str
+    score_with_o: float = 0.0  # 与原始问题的评分
+    reason: str = ""  # 评分理由
+    from_o: str = ""  # 原始问题
+
+
+class Word(BaseModel):
+    """词"""
+    text: str
+    score_with_o: float = 0.0  # 与原始问题的评分
+    from_o: str = ""  # 原始问题
+
+
+class QFromQ(BaseModel):
+    """Q来源信息(用于Sug中记录)"""
+    text: str
+    score_with_o: float = 0.0
+
+
+class Q(BaseModel):
+    """查询"""
+    text: str
+    score_with_o: float = 0.0  # 与原始问题的评分
+    reason: str = ""  # 评分理由
+    from_source: str = ""  # seg/sug/add(加词)
+
+
+class Sug(BaseModel):
+    """建议词"""
+    text: str
+    score_with_o: float = 0.0  # 与原始问题的评分
+    reason: str = ""  # 评分理由
+    from_q: QFromQ | None = None  # 来自的q
+
+
+class Seed(BaseModel):
+    """种子"""
+    text: str
+    added_words: list[str] = Field(default_factory=list)  # 已经增加的words
+    from_type: str = ""  # seg/sug/add
+    score_with_o: float = 0.0  # 与原始问题的评分
+
+
+class Post(BaseModel):
+    """帖子"""
+    title: str = ""
+    body_text: str = ""
+    type: str = "normal"  # video/normal
+    images: list[str] = Field(default_factory=list)  # 图片url列表,第一张为封面
+    video: str = ""  # 视频url
+    interact_info: dict = Field(default_factory=dict)  # 互动信息
+    note_id: str = ""
+    note_url: str = ""
+
+
+class Search(Sug):
+    """搜索结果(继承Sug)"""
+    post_list: list[Post] = Field(default_factory=list)  # 搜索得到的帖子列表
+
+
+class RunContext(BaseModel):
+    """运行上下文"""
+    version: str
+    input_files: dict[str, str]
+    c: str  # 原始需求
+    o: str  # 原始问题
+    log_url: str
+    log_dir: str
+
+    # 每轮的数据
+    rounds: list[dict] = Field(default_factory=list)  # 每轮的详细数据
+
+    # 最终结果
+    final_output: str | None = None
+
+    # 评估缓存:避免重复评估相同文本
+    evaluation_cache: dict[str, tuple[float, str]] = Field(default_factory=dict)
+    # key: 文本, value: (score, reason)
+
+
+# ============================================================================
+# Agent 定义
+# ============================================================================
+
+# Agent 1: 分词专家
+class WordSegmentation(BaseModel):
+    """分词结果"""
+    words: list[str] = Field(..., description="分词结果列表")
+    reasoning: str = Field(..., description="分词理由")
+
+word_segmentation_instructions = """
+你是分词专家。给定一个query,将其拆分成有意义的最小单元。
+
+## 分词原则
+1. 保留有搜索意义的词汇
+2. 拆分成独立的概念
+3. 保留专业术语的完整性
+4. 去除虚词(的、吗、呢等)
+
+## 输出要求
+返回分词列表和分词理由。
+""".strip()
+
+word_segmenter = Agent[None](
+    name="分词专家",
+    instructions=word_segmentation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=WordSegmentation,
+)
+
+
+# Agent 2: 动机维度评估专家 + 品类维度评估专家(两阶段评估)
+
+# 动机评估的嵌套模型
+class CoreMotivationExtraction(BaseModel):
+    """核心动机提取"""
+    简要说明核心动机: str = Field(..., description="核心动机说明")
+
+class MotivationEvaluation(BaseModel):
+    """动机维度评估"""
+    原始问题核心动机提取: CoreMotivationExtraction = Field(..., description="原始问题核心动机提取")
+    动机维度得分: float = Field(..., description="动机维度得分 -1~1")
+    简要说明动机维度相关度理由: str = Field(..., description="动机维度相关度理由")
+
+class CategoryEvaluation(BaseModel):
+    """品类维度评估"""
+    品类维度得分: float = Field(..., description="品类维度得分 -1~1")
+    简要说明品类维度相关度理由: str = Field(..., description="品类维度相关度理由")
+
+# 动机评估 prompt - 第一轮版本(来自 sug_v6_1_2_115.py)
+motivation_evaluation_instructions_round1 = """
+#角色
+你是一个 **专业的语言专家和语义相关性评判专家**。你的任务是:判断我给你的 <平台sug词条> 与 <原始问题> 的需求动机匹配度,给出 **-1 到 1 之间** 的数值评分。
+
+---
+
+# 核心概念与方法论
+
+## 评估维度
+本评估系统围绕 **动机维度** 进行:
+
+### 1. 动机维度
+**定义:** 用户"想要做什么",即原始问题的行为意图和目的
+- 核心是 **动词**:获取、学习、拍摄、制作、寻找等
+- 包括:核心动作 + 使用场景 + 最终目的
+
+---
+
+## 如何识别原始问题的核心动机
+
+**核心动机必须是动词**,识别方法如下:
+
+### 方法1: 显性动词直接提取
+
+当原始问题明确包含动词时,直接提取
+示例:
+"如何获取素材" → 核心动机 = "获取"
+"寻找拍摄技巧" → 核心动机 = "寻找"(或"学习")
+"制作视频教程" → 核心动机 = "制作"
+
+### 方法2: 隐性动词语义推理
+
+当原始问题没有显性动词时,需要结合上下文推理
+示例:
+例: "川西秋天风光摄影" → 隐含动作="拍摄"
+→ 需结合上下文判断
+
+如果原始问题是纯名词短语,无任何动作线索:
+→ 核心动机 = 无法识别
+→ 在此情况下,动机维度得分应为 0。
+示例:
+"摄影" → 无法识别动机,动机维度得分 = 0
+"川西风光" → 无法识别动机,动机维度得分 = 0
+
+---
+
+# 输入信息
+你将接收到以下输入:
+- **<原始问题>**:用户的初始查询问题,代表用户的真实需求意图。
+- **<平台sug词条>**:平台推荐的词条列表,每个词条需要单独评估。
+
+
+#判定流程
+#评估架构
+
+输入: <原始问题> + <平台sug词条>
+         ↓
+【动机维度相关性判定】
+    ├→ 步骤1: 评估<sug词条>与<原始问题>的需求动机匹配度
+    └→ 输出: -1到1之间的数值 + 判定依据
+
+
+相关度评估维度详解
+维度1: 动机维度评估
+评估对象: <平台sug词条> 与 <原始问题> 的需求动机匹配度
+说明: 核心动作是用户需求的第一优先级,决定了推荐的基本有效性
+
+
+评分标准:
+
+【正向匹配】
++0.95~1.0: 核心动作完全一致
+  - 例: 原始问题"如何获取素材" vs sug词"素材获取方法"
+  - 特殊规则: 如果sug词的核心动作是原始问题动作的**具体化子集**,也判定为完全一致
+    · 例: 原始问题"扣除猫咪主体的方法" vs sug词"扣除猫咪眼睛的方法"(子集但目的一致)
+
++0.75~0.95: 核心动作语义相近或为同义表达
+  - 例: 原始问题"如何获取素材" vs sug词"如何下载素材"
+  - 同义词对: 获取≈下载≈寻找, 技巧≈方法≈教程≈攻略
+
++0.5~0.75: 核心动作相关但非直接对应(相关实现路径)
+  - 例: 原始问题"如何获取素材" vs sug词"素材管理整理"
+
++0.2~0.45: 核心动作弱相关(同领域不同动作)
+  - 例: 原始问题"如何拍摄风光" vs sug词"风光摄影欣赏"
+
+【中性/无关】
+0: 没有明确目的,动作意图无明确关联
+  - 例: 原始问题"如何获取素材" vs sug词"摄影器材推荐"
+  - 例: 原始问题无法识别动机 且 sug词也无明确动作 → 0
+  - 如果原始问题无法识别动机,则动机维度得分为0。
+
+【负向偏离】
+-0.2~-0.05: 动作意图轻度冲突或误导
+  - 例: 原始问题"如何获取素材" vs sug词"素材版权保护须知"
+
+-0.5~-0.25: 动作意图明显对立
+  - 例: 原始问题"如何获取免费素材" vs sug词"如何售卖素材"
+
+-1.0~-0.55: 动作意图完全相反或产生严重负面引导
+  - 例: 原始问题"免费素材获取" vs sug词"付费素材强制推销"
+
+---
+
+# 输出要求
+
+输出结果必须为一个 **JSON 格式**,包含以下内容:
+```json
+{
+  "原始问题核心动机提取": {
+    "简要说明核心动机": ""
+  },
+  "动机维度得分": "-1到1之间的小数",
+  "简要说明动机维度相关度理由": "评估该sug词条与原始问题动机匹配程度的理由"
+}
+
+**输出约束(非常重要)**:
+1. **字符串长度限制**:\"简要说明动机维度相关度理由\"字段必须控制在**150字以内**
+2. **JSON格式规范**:必须生成完整的JSON格式,确保字符串用双引号包裹且正确闭合
+3. **引号使用**:字符串中如需表达引用,请使用《》或「」代替单引号或双引号
+
+#注意事项:
+始终围绕动机维度:所有评估都基于"动机"维度,不偏离
+核心动机必须是动词:在评估前,必须先提取原始问题的核心动机(动词),这是整个评估的基础
+严格标准一致性:对所有用例使用相同的评估标准,避免评分飘移
+负分使用原则:仅当sug词条对原始问题动机产生误导、冲突或有害引导时给予负分
+零分使用原则:当sug词条与原始问题动机无明确关联,既不相关也不冲突时给予零分,或原始问题无法识别动机时。
+""".strip()
+
+# 动机评估 prompt - 后续轮次版本(当前 116 版本)
+motivation_evaluation_instructions = """
+#角色
+你是一个 **专业的语言专家和语义相关性评判专家**。你的任务是:判断我给你的 <平台sug词条> 与 <原始问题> 的需求动机匹配度,给出 **-1 到 1 之间** 的数值评分。
+
+---
+# 动机评估核心原则(必读)
+
+### 动机 = 动作 + 对象 + 场景
+评估时必须同时考虑三要素,不能只看动词:
+- **动作**:制定、规划、获取、拍摄等
+- **对象**:旅行行程 vs 每日计划、风光照片 vs 证件照
+- **场景**:旅游 vs 日常、摄影 vs 办公
+
+### 关键判断:动词相同 ≠ 动机匹配
+
+错误:只看动词相同就给高分
+- "制定旅行行程" vs "制定每日计划" → 给0.95 错误
+- "拍摄风光" vs "拍摄证件照" → 给0.95 错误
+
+正确:检查对象和场景是否匹配
+- 对象不同领域 → 降至0.3左右
+- 场景不同 → 降至0.3左右
+
+
+# 核心概念与方法论
+
+## 评估维度
+本评估系统围绕 **动机维度** 进行:
+
+# 维度独立性警告
+【严格约束】本评估**只评估动机维度**:
+**禁止使用"主题相关"作为评分依据**:评分理由中不得出现"主题"、"内容"、"话题"等词
+
+### 1. 动机维度
+**定义:** 用户"想要做什么",即原始问题的行为意图和目的
+- 核心是 **动词**:获取、学习、拍摄、制作、寻找等
+- 包括:核心动作 + 使用场景 + 最终目的
+
+---
+
+如果原始问题是纯名词短语,无任何动作线索:
+→ 核心动机 = 无法识别
+→ 在此情况下,动机维度得分应为 0。
+示例:
+"摄影" → 无法识别动机,动机维度得分 = 0
+"川西风光" → 无法识别动机,动机维度得分 = 0
+
+---
+
+# 输入信息
+你将接收到以下输入:
+- **<原始问题>**:用户的初始查询问题,代表用户的真实需求意图。
+- **<平台sug词条>**:平台推荐的词条列表,每个词条需要单独评估。
+
+
+#判定流程
+#评估架构
+
+输入: <原始问题> + <平台sug词条>
+         ↓
+【动机维度相关性判定】
+    ├→ 步骤1: 评估<sug词条>与<原始问题>的需求动机匹配度
+    └→ 输出: -1到1之间的数值 + 判定依据
+
+
+相关度评估维度详解
+维度1: 动机维度评估
+评估对象: <平台sug词条> 与 <原始问题> 的需求动机匹配度
+说明: 核心动作是用户需求的第一优先级,决定了推荐的基本有效性
+
+
+评分标准:
+
+【正向匹配】
++0.95~1.0: 动作+对象+场景完全一致
+  - 要求:动词、对象、场景都必须匹配,不能只看动词
+  - "制定旅行行程" vs "制定每日计划"
+     虽然动词相同,但对象和场景完全不同,不属于高分
+  - 特殊规则: 如果sug词的核心动作是原始问题动作在动作+对象+场景一致下的**具体化子集**,也判定为完全一致
+
+
++0.75~0.95: 核心动作语义相近或为同义表达
+  - 例: 原始问题"如何获取素材" vs sug词"如何下载素材"
+  - 同义词对: 获取≈下载≈寻找, 技巧≈方法≈教程≈攻略
+
++0.5~0.75: 核心动作相关但非直接对应(相关实现路径)
+  - 例: 原始问题"如何获取素材" vs sug词"素材管理整理"
+
++0.25~0.4: 动词相同但对象或场景明显不同(弱相关)
+  - 判断要点:动词一致,但对象不同领域或场景不同
+  - 关键:不要因为动词相同就给0.95,必须检查对象!
+
+
+【中性/无关】
+0: 没有明确目的,动作意图无明确关联
+  - 例: 原始问题"如何获取素材" vs sug词"摄影器材推荐"
+  - 例: 原始问题无法识别动机 且 sug词也无明确动作 → 0
+  - 如果原始问题无法识别动机,则动机维度得分为0
+
+ 特别注意 - 禁止的错误理由:
+  - 禁止: "虽然没有动作,但主题相关,所以给0.2"
+  - 禁止:"内容有参考价值,所以给0.15"
+  - 禁止: "都提到了XX(名词),所以不是完全无关"
+  - 正确理由:"sug词条无动作意图,与原始问题的'XX'动机完全无关"
+
+【负向偏离】
+-0.2~-0.05: 动作意图轻度冲突或误导
+  - 例: 原始问题"如何获取素材" vs sug词"素材版权保护须知"
+
+-0.5~-0.25: 动作意图明显对立
+  - 例: 原始问题"如何获取免费素材" vs sug词"如何售卖素材"
+
+-1.0~-0.55: 动作意图完全相反或产生严重负面引导
+  - 例: 原始问题"免费素材获取" vs sug词"付费素材强制推销"
+
+---
+
+# 输出要求
+
+输出结果必须为一个 **JSON 格式**,包含以下内容:
+```json
+{
+  "原始问题核心动机提取": {
+    "简要说明核心动机": ""
+  },
+  "动机维度得分": "-1到1之间的小数",
+  "简要说明动机维度相关度理由": "评估该sug词条与原始问题动机匹配程度的理由"
+}
+
+**输出约束(非常重要)**:
+1. **字符串长度限制**:\"简要说明动机维度相关度理由\"字段必须控制在**150字以内**
+2. **JSON格式规范**:必须生成完整的JSON格式,确保字符串用双引号包裹且正确闭合
+3. **引号使用**:字符串中如需表达引用,请使用《》或「」代替单引号或双引号
+
+#注意事项:
+始终围绕动机维度:所有评估都基于"动机"维度,不偏离
+核心动机必须是动词:在评估前,必须先提取原始问题的核心动机(动词),这是整个评估的基础
+严格标准一致性:对所有用例使用相同的评估标准,避免评分飘移
+负分使用原则:仅当sug词条对原始问题动机产生误导、冲突或有害引导时给予负分
+零分使用原则:当sug词条与原始问题动机无明确关联,既不相关也不冲突时给予零分,或原始问题无法识别动机时。
+""".strip()
+
+# 品类评估 prompt
+category_evaluation_instructions = """
+#角色
+你是一个 **专业的语言专家和语义相关性评判专家**。你的任务是:判断我给你的 <平台sug词条> 与 <原始问题> 的内容主体和限定词匹配度,给出 **-1 到 1 之间** 的数值评分。
+
+---
+# 核心概念与方法论
+
+## 评估维度
+本评估系统围绕 **品类维度** 进行:
+
+#  维度独立性警告
+【严格约束】本评估**只评估品类维度**,,必须遵守以下规则:
+1. **只看名词和限定词**:评估时只考虑主体、限定词的匹配度
+2. **完全忽略动词**:动作意图、目的等动机信息对本维度评分无影响
+
+### 品类维度
+**定义:** 用户"关于什么内容",即原始问题的主题对象和限定词
+- 核心是 **名词+限定词**:川西秋季风光摄影素材
+- 包括:核心主体 + 地域限定 + 时间限定 + 质量限定等
+
+## ⚠️ 品类评估核心原则(必读)
+
+### 原则1:只看词条表面,禁止联想推演
+- 只能基于sug词实际包含的词汇评分
+- 禁止推测"可能包含"、"可以理解为"
+
+**错误示例:**
+原始问题:"川西旅行行程" vs sug词:"每日计划"
+- 错误 "每日计划可以包含旅行规划,所以有关联" → 这是不允许的联想
+- 正确: "sug词只有'每日计划',无'旅行'字眼,品类不匹配" → 正确判断
+
+### 原则2:通用概念 ≠ 特定概念
+- **通用**:计划、方法、技巧、素材(无领域限定)
+- **特定**:旅行行程、摄影技巧、烘焙方法(有明确领域)
+
+IF sug词是通用 且 原始问题是特定:
+   → 品类不匹配 → 评分0.05~0.1
+关键:通用概念不等于特定概念,不能因为"抽象上都是规划"就给分
+
+---
+
+# 输入信息
+你将接收到以下输入:
+- **<原始问题>**:用户的初始查询问题,代表用户的真实需求意图。
+- **<平台sug词条>**:平台推荐的词条列表,每个词条需要单独评估。
+
+
+#判定流程
+#评估架构
+
+输入: <原始问题> + <平台sug词条>
+         ↓
+【品类维度相关性判定】
+    ├→ 步骤1: 评估<sug词条>与<原始问题>的内容主体和限定词匹配度
+    └→ 输出: -1到1之间的数值 + 判定依据
+
+
+相关度评估维度详解
+维度2: 品类维度评估
+评估对象: <平台sug词条> 与 <原始问题> 的内容主体和限定词匹配度
+
+评分标准:
+
+【正向匹配】
++0.95~1.0: 核心主体+所有关键限定词完全匹配
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"川西秋季风光摄影作品"
+
++0.75~0.95: 核心主体匹配,存在限定词匹配
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"川西风光摄影素材"(缺失"秋季")
+
++0.5~0.75: 核心主体匹配,无限定词匹配或合理泛化
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"四川风光摄影"
+
++0.3~0.5: 核心主体匹配,但限定词缺失或存在语义错位
+  - 特别注意"语义身份"差异,主体词出现但上下文语义不同
+  - 例:
+    · "猫咪的XX行为"(猫咪是行为者)
+    · vs "用猫咪表达XX的梗图"(猫咪是媒介)
+    · 虽都含"猫咪+XX",但语义角色不同
+
++0.2~0.3: 主体词不匹配,限定词缺失或错位
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"风光摄影入门"
+
++0.05~0.2: 主体词过度泛化或仅抽象相似
+  - 例: sug词是通用概念,原始问题是特定概念
+    sug词"每日计划"(通用)vs 原始问题 "川西旅行行程"(特定)
+      → 评分:0.08
+
+【中性/无关】
+0: 类别明显不同,没有明确目的,无明确关联
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"人像摄影素材"
+  - 例: 原始问题无法识别动机 且 sug词也无明确动作 → 0
+
+【负向偏离】
+-0.2~-0.05: 主体词或限定词存在误导性
+  - 例: 原始问题"免费摄影素材" vs sug词"付费摄影素材库"
+
+-0.5~-0.25: 主体词明显错位或品类冲突
+  - 例: 原始问题"风光摄影素材" vs sug词"人像修图教程"
+
+-1.0~-0.55: 完全错误的品类或有害引导
+  - 例: 原始问题"正版素材获取" vs sug词"盗版素材下载"
+
+---
+
+# 输出要求
+
+输出结果必须为一个 **JSON 格式**,包含以下内容:
+```json
+{
+  "品类维度得分": "-1到1之间的小数",
+  "简要说明品类维度相关度理由": "评估该sug词条与原始问题品类匹配程度的理由"
+}
+---
+
+**输出约束(非常重要)**:
+1. **字符串长度限制**:\"简要说明品类维度相关度理由\"字段必须控制在**150字以内**
+2. **JSON格式规范**:必须生成完整的JSON格式,确保字符串用双引号包裹且正确闭合
+3. **引号使用**:字符串中如需表达引用,请使用《》或「」代替单引号或双引号
+
+---
+
+#注意事项:
+始终围绕品类维度:所有评估都基于"品类"维度,不偏离
+严格标准一致性:对所有用例使用相同的评估标准,避免评分飘移
+负分使用原则:仅当sug词条对原始问题品类产生误导、冲突或有害引导时给予负分
+零分使用原则:当sug词条与原始问题品类无明确关联,既不相关也不冲突时给予零分
+""".strip()
+
+# 创建评估 Agent - 第一轮使用
+motivation_evaluator_round1 = Agent[None](
+    name="动机维度评估专家(第一轮)",
+    instructions=motivation_evaluation_instructions_round1,
+    model=get_model(MODEL_NAME),
+    output_type=MotivationEvaluation)
+
+# 创建评估 Agent - 后续轮次使用
+motivation_evaluator = Agent[None](
+    name="动机维度评估专家(后续轮次)",
+    instructions=motivation_evaluation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=MotivationEvaluation)
+
+category_evaluator = Agent[None](
+    name="品类维度评估专家",
+    instructions=category_evaluation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=CategoryEvaluation
+)
+
+
+# Agent 3: 加词选择专家
+class WordCombination(BaseModel):
+    """单个词组合"""
+    selected_word: str = Field(..., description="选择的词")
+    combined_query: str = Field(..., description="组合后的新query")
+    reasoning: str = Field(..., description="选择理由")
+
+class WordSelectionTop5(BaseModel):
+    """加词选择结果(Top 5)"""
+    combinations: list[WordCombination] = Field(
+        ...,
+        description="选择的Top 5组合(不足5个则返回所有)",
+        min_items=1,
+        max_items=5
+    )
+    overall_reasoning: str = Field(..., description="整体选择思路")
+
+word_selection_instructions = """
+你是加词组合专家。
+
+## 任务
+从候选词列表中选择5个最合适的词,分别与当前seed组合成新的query。如果候选词不足5个,则返回所有。
+
+## 选择原则
+1. **相关性**:选择与当前seed最相关的词
+2. **语义通顺**:组合后的query要符合搜索习惯
+3. **扩展范围**:优先选择能扩展搜索范围的词
+4. **多样性**:5个词应该覆盖不同的方面(如:时间、地点、类型、用途等)
+
+## 组合约束(严格执行)
+**CRITICAL: 以下约束必须100%遵守,违反任何一条都是错误**
+
+1. **必须完整保留seed的所有文本内容**
+   - seed的每一个字都必须出现在组合结果中
+   - 禁止删除、省略、替换seed中的任何部分
+   - 即使某些字看起来不重要,也必须保留
+
+2. **必须完整保留word的所有文本内容**
+   - word的每一个字都必须出现在组合结果中
+   - 禁止删除、省略、替换word中的任何部分
+
+3. **禁止添加任何额外内容**
+   - 不能添加连接词(如"的"、"和"、"与"、"在"等)
+   - 不能添加任何其他词或字符
+
+4. **组合方式仅限以下三种**
+   - seed在前:seed的文本 + word的文本(如:制作梗图 + 猫咪 = 制作梗图猫咪)
+   - word在前:word的文本 + seed的文本(如:猫咪 + 制作梗图 = 猫咪制作梗图)
+   - word插入:将word插入seed中间合适位置(如:制作 + 猫咪 + 梗图 = 制作猫咪梗图)
+
+5. **验证检查清单**(在输出前必须自查)
+   ☑ 组合结果包含seed的所有字符?
+   ☑ 组合结果包含word的所有字符?
+   ☑ 组合结果没有额外的字符?
+   ☑ 只使用了三种组合方式之一?
+
+## 正确示例(必须参考)
+✓ seed="制作梗图" + word="猫咪" → "制作梗图猫咪"(seed在前)
+✓ seed="制作梗图" + word="猫咪" → "猫咪制作梗图"(word在前)
+✓ seed="制作梗图" + word="猫咪" → "制作猫咪梗图"(word插入中间)
+✓ seed="川西" + word="秋季" → "川西秋季"(seed在前)
+✓ seed="川西" + word="秋季" → "秋季川西"(word在前)
+✓ seed="摄影" + word="技巧" → "摄影技巧"(seed在前)
+✓ seed="摄影" + word="技巧" → "技巧摄影"(word在前)
+
+## 错误示例(严禁出现)
+✗ seed="制作梗图" + word="猫咪" → "猫咪梗图"(❌ 缺少"制作")
+✗ seed="制作梗图" + word="猫咪" → "梗图猫咪"(❌ 缺少"制作")
+✗ seed="制作梗图" + word="猫咪" → "制作猫咪表情包"(❌ 加了"表情包")
+✗ seed="川西" + word="秋季" → "川西的秋季"(❌ 加了"的")
+✗ seed="川西" + word="秋季" → "川西秋季风光"(❌ 加了"风光")
+✗ seed="摄影" + word="技巧" → "摄影拍摄技巧"(❌ 加了"拍摄")
+✗ seed="摄影" + word="技巧" → "影技巧"(❌ 缺少"摄")
+
+## 输出要求
+- 最多返回5个组合(如果候选词不足5个,返回所有)
+- 每个组合包含:
+  * selected_word: 选择的词(必须在候选词列表中)
+  * combined_query: 组合后的新query(只包含seed和word的原始文本,不多不少)
+  * reasoning: 选择理由(说明为什么选这个词)
+- overall_reasoning: 整体选择思路(说明这5个词的选择逻辑)
+
+## JSON输出规范
+1. **格式要求**:必须输出标准的、完整的JSON格式
+2. **字符限制**:不要在JSON中使用任何不可见的特殊字符或控制字符
+3. **引号规范**:字符串中如需表达引用或强调,使用书名号《》或单书名号「」,不要使用英文引号或中文引号""
+4. **编码规范**:所有文本使用UTF-8编码,不要包含二进制或转义序列
+5. **完整性**:确保JSON的开始和结束括号完整匹配,所有字段都正确闭合
+""".strip()
+
+word_selector = Agent[None](
+    name="加词组合专家",
+    instructions=word_selection_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=WordSelectionTop5,
+    model_settings=ModelSettings(temperature=0.2),
+)
+
+
+# ============================================================================
+# 辅助函数
+# ============================================================================
+
+def calculate_final_score(motivation_score: float, category_score: float) -> float:
+    """
+    应用依存性规则计算最终得分
+
+    步骤1: 基础加权计算
+    base_score = motivation_score * 0.7 + category_score * 0.3
+
+    步骤2: 极值保护规则
+
+    Args:
+        motivation_score: 动机维度得分 -1~1
+        category_score: 品类维度得分 -1~1
+
+    Returns:
+        最终得分 -1~1
+    """
+    # 基础加权得分
+    base_score = motivation_score * 0.7 + category_score * 0.3
+
+    # 规则C: 动机负向决定机制(最高优先级)
+    if motivation_score < 0:
+        return 0.0
+
+    # 规则A: 动机高分保护机制
+    if motivation_score >= 0.8:
+        # 当目的高度一致时,品类的泛化不应导致"弱相关"
+        return max(base_score, 0.7)
+
+    # 规则B: 动机低分限制机制
+    if motivation_score <= 0.2:
+        # 目的不符时,品类匹配的价值有限
+        return min(base_score, 0.5)
+
+    # 无规则调整,返回基础得分
+    return base_score
+
+
+def clean_json_string(text: str) -> str:
+    """清理JSON中的非法控制字符(保留 \t \n \r)"""
+    import re
+    # 移除除了 \t(09) \n(0A) \r(0D) 之外的所有控制字符
+    return re.sub(r'[\x00-\x08\x0B\x0C\x0E-\x1F]', '', text)
+
+
+def process_note_data(note: dict) -> Post:
+    """处理搜索接口返回的帖子数据"""
+    note_card = note.get("note_card", {})
+    image_list = note_card.get("image_list", [])
+    interact_info = note_card.get("interact_info", {})
+    user_info = note_card.get("user", {})
+
+    # ========== 调试日志 START ==========
+    note_id = note.get("id", "")
+    raw_title = note_card.get("display_title")  # 不提供默认值
+    raw_body = note_card.get("desc")
+    raw_type = note_card.get("type")
+
+    # 打印原始值类型和内容
+    print(f"\n[DEBUG] 处理帖子 {note_id}:")
+    print(f"  raw_title 类型: {type(raw_title).__name__}, 值: {repr(raw_title)}")
+    print(f"  raw_body 类型: {type(raw_body).__name__}, 值: {repr(raw_body)[:100] if raw_body else repr(raw_body)}")
+    print(f"  raw_type 类型: {type(raw_type).__name__}, 值: {repr(raw_type)}")
+
+    # 检查是否为 None
+    if raw_title is None:
+        print(f"  ⚠️  WARNING: display_title 是 None!")
+    if raw_body is None:
+        print(f"  ⚠️  WARNING: desc 是 None!")
+    if raw_type is None:
+        print(f"  ⚠️  WARNING: type 是 None!")
+    # ========== 调试日志 END ==========
+
+    # 提取图片URL - 使用新的字段名 image_url
+    images = []
+    for img in image_list:
+        if isinstance(img, dict):
+            # 尝试新字段名 image_url,如果不存在则尝试旧字段名 url_default
+            img_url = img.get("image_url") or img.get("url_default")
+            if img_url:
+                images.append(img_url)
+
+    # 判断类型
+    note_type = note_card.get("type", "normal")
+    video_url = ""
+    if note_type == "video":
+        video_info = note_card.get("video", {})
+        if isinstance(video_info, dict):
+            # 尝试获取视频URL
+            video_url = video_info.get("media", {}).get("stream", {}).get("h264", [{}])[0].get("master_url", "")
+
+    return Post(
+        note_id=note.get("id") or "",
+        title=note_card.get("display_title") or "",
+        body_text=note_card.get("desc") or "",
+        type=note_type,
+        images=images,
+        video=video_url,
+        interact_info={
+            "liked_count": interact_info.get("liked_count", 0),
+            "collected_count": interact_info.get("collected_count", 0),
+            "comment_count": interact_info.get("comment_count", 0),
+            "shared_count": interact_info.get("shared_count", 0)
+        },
+        note_url=f"https://www.xiaohongshu.com/explore/{note.get('id', '')}"
+    )
+
+
+async def evaluate_with_o(text: str, o: str, cache: dict[str, tuple[float, str]] | None = None, round_num: int = 1) -> tuple[float, str]:
+    """评估文本与原始问题o的相关度
+
+    采用两阶段评估 + 代码计算规则:
+    1. 动机维度评估(权重70%)
+    2. 品类维度评估(权重30%)
+    3. 应用规则A/B/C调整得分
+
+    Args:
+        text: 待评估的文本
+        o: 原始问题
+        cache: 评估缓存(可选),用于避免重复评估
+        round_num: 轮次编号,第一轮使用 round1 prompt,后续使用标准 prompt
+
+    Returns:
+        tuple[float, str]: (最终相关度分数, 综合评估理由)
+    """
+    # 检查缓存
+    if cache is not None and text in cache:
+        cached_score, cached_reason = cache[text]
+        print(f"  ⚡ 缓存命中: {text} -> {cached_score:.2f}")
+        return cached_score, cached_reason
+
+    # 准备输入
+    eval_input = f"""
+<原始问题>
+{o}
+</原始问题>
+
+<平台sug词条>
+{text}
+</平台sug词条>
+
+请评估平台sug词条与原始问题的匹配度。
+"""
+
+    # 添加重试机制
+    max_retries = 2
+    last_error = None
+
+    for attempt in range(max_retries):
+        try:
+            # 根据轮次选择不同的 motivation evaluator
+            # 第一轮使用 round1 版本,后续使用标准版本
+            selected_motivation_evaluator = motivation_evaluator_round1 if round_num == 1 else motivation_evaluator
+
+            # 并发调用两个评估器
+            motivation_task = Runner.run(selected_motivation_evaluator, eval_input)
+            category_task = Runner.run(category_evaluator, eval_input)
+
+            motivation_result, category_result = await asyncio.gather(
+                motivation_task,
+                category_task
+            )
+
+            # 获取评估结果
+            motivation_eval: MotivationEvaluation = motivation_result.final_output
+            category_eval: CategoryEvaluation = category_result.final_output
+
+            # 提取得分
+            motivation_score = motivation_eval.动机维度得分
+            category_score = category_eval.品类维度得分
+
+            # 计算基础得分
+            base_score = motivation_score * 0.7 + category_score * 0.3
+
+            # 应用规则计算最终得分
+            final_score = calculate_final_score(motivation_score, category_score)
+
+            # 组合评估理由
+            core_motivation = motivation_eval.原始问题核心动机提取.简要说明核心动机
+            motivation_reason = motivation_eval.简要说明动机维度相关度理由
+            category_reason = category_eval.简要说明品类维度相关度理由
+
+            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}】"
+            )
+
+            # 如果应用了规则,添加规则说明
+            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:动机低分限制机制)"
+
+            # 存入缓存
+            if cache is not None:
+                cache[text] = (final_score, combined_reason)
+
+            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
+
+
+# ============================================================================
+# 核心流程函数
+# ============================================================================
+
+async def initialize(o: str, context: RunContext) -> tuple[list[Seg], list[Word], list[Q], list[Seed]]:
+    """
+    初始化阶段
+
+    Returns:
+        (seg_list, word_list_1, q_list_1, seed_list)
+    """
+    print(f"\n{'='*60}")
+    print(f"初始化阶段")
+    print(f"{'='*60}")
+
+    # 1. 分词:原始问题(o) ->分词-> seg_list
+    print(f"\n[步骤1] 分词...")
+    result = await Runner.run(word_segmenter, o)
+    segmentation: WordSegmentation = result.final_output
+
+    seg_list = []
+    for word in segmentation.words:
+        seg_list.append(Seg(text=word, from_o=o))
+
+    print(f"分词结果: {[s.text for s in seg_list]}")
+    print(f"分词理由: {segmentation.reasoning}")
+
+    # 2. 分词评估:seg_list -> 每个seg与o进行评分(使用信号量限制并发数)
+    print(f"\n[步骤2] 评估每个分词与原始问题的相关度...")
+
+    MAX_CONCURRENT_SEG_EVALUATIONS = 5
+    seg_semaphore = asyncio.Semaphore(MAX_CONCURRENT_SEG_EVALUATIONS)
+
+    async def evaluate_seg(seg: Seg) -> Seg:
+        async with seg_semaphore:
+            # 初始化阶段的分词评估使用第一轮 prompt (round_num=1)
+            seg.score_with_o, seg.reason = await evaluate_with_o(seg.text, o, context.evaluation_cache, round_num=1)
+            return seg
+
+    if seg_list:
+        print(f"  开始评估 {len(seg_list)} 个分词(并发限制: {MAX_CONCURRENT_SEG_EVALUATIONS})...")
+        eval_tasks = [evaluate_seg(seg) for seg in seg_list]
+        await asyncio.gather(*eval_tasks)
+
+    for seg in seg_list:
+        print(f"  {seg.text}: {seg.score_with_o:.2f}")
+
+    # 3. 构建word_list_1: seg_list -> word_list_1(固定词库)
+    print(f"\n[步骤3] 构建word_list_1(固定词库)...")
+    word_list_1 = []
+    for seg in seg_list:
+        word_list_1.append(Word(
+            text=seg.text,
+            score_with_o=seg.score_with_o,
+            from_o=o
+        ))
+    print(f"word_list_1(固定): {[w.text for w in word_list_1]}")
+
+    # 4. 构建q_list_1:seg_list 作为 q_list_1
+    print(f"\n[步骤4] 构建q_list_1...")
+    q_list_1 = []
+    for seg in seg_list:
+        q_list_1.append(Q(
+            text=seg.text,
+            score_with_o=seg.score_with_o,
+            reason=seg.reason,
+            from_source="seg"
+        ))
+    print(f"q_list_1: {[q.text for q in q_list_1]}")
+
+    # 5. 构建seed_list: seg_list -> seed_list
+    print(f"\n[步骤5] 构建seed_list...")
+    seed_list = []
+    for seg in seg_list:
+        seed_list.append(Seed(
+            text=seg.text,
+            added_words=[],
+            from_type="seg",
+            score_with_o=seg.score_with_o
+        ))
+    print(f"seed_list: {[s.text for s in seed_list]}")
+
+    return seg_list, word_list_1, q_list_1, seed_list
+
+
+async def run_round(
+    round_num: int,
+    q_list: list[Q],
+    word_list_1: list[Word],
+    seed_list: list[Seed],
+    o: str,
+    context: RunContext,
+    xiaohongshu_api: XiaohongshuSearchRecommendations,
+    xiaohongshu_search: XiaohongshuSearch,
+    sug_threshold: float = 0.7
+) -> tuple[list[Q], list[Seed], list[Search]]:
+    """
+    运行一轮
+
+    Args:
+        round_num: 轮次编号
+        q_list: 当前轮的q列表
+        word_list_1: 固定的词库(第0轮分词结果)
+        seed_list: 当前的seed列表
+        o: 原始问题
+        context: 运行上下文
+        xiaohongshu_api: 建议词API
+        xiaohongshu_search: 搜索API
+        sug_threshold: suggestion的阈值
+
+    Returns:
+        (q_list_next, seed_list_next, search_list)
+    """
+    print(f"\n{'='*60}")
+    print(f"第{round_num}轮")
+    print(f"{'='*60}")
+
+    round_data = {
+        "round_num": round_num,
+        "input_q_list": [{"text": q.text, "score": q.score_with_o, "type": "query"} for q in q_list],
+        "input_word_list_1_size": len(word_list_1),
+        "input_seed_list_size": len(seed_list)
+    }
+
+    # 1. 请求sug:q_list -> 每个q请求sug接口 -> sug_list_list
+    print(f"\n[步骤1] 为每个q请求建议词...")
+    sug_list_list = []  # list of list
+    for q in q_list:
+        print(f"\n  处理q: {q.text}")
+        suggestions = xiaohongshu_api.get_recommendations(keyword=q.text)
+
+        q_sug_list = []
+        if suggestions:
+            print(f"    获取到 {len(suggestions)} 个建议词")
+            for sug_text in suggestions:
+                sug = Sug(
+                    text=sug_text,
+                    from_q=QFromQ(text=q.text, score_with_o=q.score_with_o)
+                )
+                q_sug_list.append(sug)
+        else:
+            print(f"    未获取到建议词")
+
+        sug_list_list.append(q_sug_list)
+
+    # 2. sug评估:sug_list_list -> 每个sug与o进行评分(并发)
+    print(f"\n[步骤2] 评估每个建议词与原始问题的相关度...")
+
+    # 2.1 收集所有需要评估的sug,并记录它们所属的q
+    all_sugs = []
+    sug_to_q_map = {}  # 记录每个sug属于哪个q
+    for i, q_sug_list in enumerate(sug_list_list):
+        if q_sug_list:
+            q_text = q_list[i].text
+            for sug in q_sug_list:
+                all_sugs.append(sug)
+                sug_to_q_map[id(sug)] = q_text
+
+    # 2.2 并发评估所有sug(使用信号量限制并发数)
+    # 每个 evaluate_sug 内部会并发调用 2 个 LLM,所以这里限制为 5,实际并发 LLM 请求为 10
+    MAX_CONCURRENT_EVALUATIONS = 5
+    semaphore = asyncio.Semaphore(MAX_CONCURRENT_EVALUATIONS)
+
+    async def evaluate_sug(sug: Sug) -> Sug:
+        async with semaphore:  # 限制并发数
+            # 根据轮次选择 prompt: 第一轮使用 round1 prompt,后续使用标准 prompt
+            sug.score_with_o, sug.reason = await evaluate_with_o(sug.text, o, context.evaluation_cache, round_num=round_num)
+            return sug
+
+    if all_sugs:
+        print(f"  开始评估 {len(all_sugs)} 个建议词(并发限制: {MAX_CONCURRENT_EVALUATIONS})...")
+        eval_tasks = [evaluate_sug(sug) for sug in all_sugs]
+        await asyncio.gather(*eval_tasks)
+
+    # 2.3 打印结果并组织到sug_details
+    sug_details = {}  # 保存每个Q对应的sug列表
+    for i, q_sug_list in enumerate(sug_list_list):
+        if q_sug_list:
+            q_text = q_list[i].text
+            print(f"\n  来自q '{q_text}' 的建议词:")
+            sug_details[q_text] = []
+            for sug in q_sug_list:
+                print(f"    {sug.text}: {sug.score_with_o:.2f}")
+                # 保存到sug_details
+                sug_details[q_text].append({
+                    "text": sug.text,
+                    "score": sug.score_with_o,
+                    "reason": sug.reason,
+                    "type": "sug"
+                })
+
+    # 2.4 剪枝判断(已禁用 - 保留所有分支)
+    pruned_query_texts = set()
+    if False:  # 原: if round_num >= 2:  # 剪枝功能已禁用,保留代码以便后续调整
+        print(f"\n[剪枝判断] 第{round_num}轮开始应用剪枝策略...")
+        for i, q in enumerate(q_list):
+            q_sug_list = sug_list_list[i]
+
+            if len(q_sug_list) == 0:
+                continue  # 没有sug则不剪枝
+
+            # 剪枝条件1: 所有sug分数都低于query分数
+            all_lower_than_query = all(sug.score_with_o < q.score_with_o for sug in q_sug_list)
+            # 剪枝条件2: 所有sug分数都低于0.5
+            all_below_threshold = all(sug.score_with_o < 0.5 for sug in q_sug_list)
+
+            if all_lower_than_query and all_below_threshold:
+                pruned_query_texts.add(q.text)
+                max_sug_score = max(sug.score_with_o for sug in q_sug_list)
+                print(f"  🔪 剪枝: {q.text} (query分数:{q.score_with_o:.2f}, sug最高分:{max_sug_score:.2f}, 全部<0.5)")
+
+        if pruned_query_texts:
+            print(f"  本轮共剪枝 {len(pruned_query_texts)} 个query")
+        else:
+            print(f"  本轮无query被剪枝")
+    else:
+        print(f"\n[剪枝判断] 剪枝功能已禁用,保留所有分支")
+
+    # 3. search_list构建
+    print(f"\n[步骤3] 构建search_list(阈值>{sug_threshold})...")
+    search_list = []
+    high_score_sugs = [sug for sug in all_sugs if sug.score_with_o > sug_threshold]
+
+    if high_score_sugs:
+        print(f"  找到 {len(high_score_sugs)} 个高分建议词")
+
+        # 并发搜索
+        async def search_for_sug(sug: Sug) -> Search:
+            print(f"    搜索: {sug.text}")
+            try:
+                search_result = xiaohongshu_search.search(keyword=sug.text)
+                result_str = search_result.get("result", "{}")
+                if isinstance(result_str, str):
+                    result_data = json.loads(result_str)
+                else:
+                    result_data = result_str
+
+                notes = result_data.get("data", {}).get("data", [])
+                post_list = []
+                for note in notes[:10]:  # 只取前10个
+                    post = process_note_data(note)
+                    post_list.append(post)
+
+                print(f"      → 找到 {len(post_list)} 个帖子")
+
+                return Search(
+                    text=sug.text,
+                    score_with_o=sug.score_with_o,
+                    from_q=sug.from_q,
+                    post_list=post_list
+                )
+            except Exception as e:
+                print(f"      ✗ 搜索失败: {e}")
+                return Search(
+                    text=sug.text,
+                    score_with_o=sug.score_with_o,
+                    from_q=sug.from_q,
+                    post_list=[]
+                )
+
+        search_tasks = [search_for_sug(sug) for sug in high_score_sugs]
+        search_list = await asyncio.gather(*search_tasks)
+    else:
+        print(f"  没有高分建议词,search_list为空")
+
+    # 4. 构建q_list_next
+    print(f"\n[步骤4] 构建q_list_next...")
+    q_list_next = []
+    existing_q_texts = set()  # 用于去重
+    add_word_details = {}  # 保存每个seed对应的组合词列表
+    all_seed_combinations = []  # 保存本轮所有seed的组合词(用于后续构建seed_list_next)
+
+    # 4.1 对于seed_list中的每个seed,从word_list_1中选词组合,产生Top 5
+    print(f"\n  4.1 为每个seed加词(产生Top 5组合)...")
+    for seed in seed_list:
+        print(f"\n    处理seed: {seed.text}")
+
+        # 剪枝检查:跳过被剪枝的seed
+        if seed.text in pruned_query_texts:
+            print(f"      ⊗ 跳过被剪枝的seed: {seed.text}")
+            continue
+
+        # 从固定词库word_list_1筛选候选词
+        candidate_words = []
+        for word in word_list_1:
+            # 检查词是否已在seed中
+            if word.text in seed.text:
+                continue
+            # 检查词是否已被添加过
+            if word.text in seed.added_words:
+                continue
+            candidate_words.append(word)
+
+        if not candidate_words:
+            print(f"      没有可用的候选词")
+            continue
+
+        print(f"      候选词数量: {len(candidate_words)}")
+
+        # 调用Agent一次性选择并组合Top 5(添加重试机制)
+        candidate_words_text = ', '.join([w.text for w in candidate_words])
+        selection_input = f"""
+<原始问题>
+{o}
+</原始问题>
+
+<当前Seed>
+{seed.text}
+</当前Seed>
+
+<候选词列表>
+{candidate_words_text}
+</候选词列表>
+
+请从候选词列表中选择最多5个最合适的词,分别与当前seed组合成新的query。
+"""
+
+        # 重试机制
+        max_retries = 2
+        selection_result = None
+        for attempt in range(max_retries):
+            try:
+                result = await Runner.run(word_selector, selection_input)
+                selection_result = result.final_output
+                break  # 成功则跳出
+            except Exception as e:
+                error_msg = str(e)
+                if attempt < max_retries - 1:
+                    print(f"      ⚠️  选词失败 (尝试 {attempt+1}/{max_retries}): {error_msg[:100]}")
+                    await asyncio.sleep(1)
+                else:
+                    print(f"      ❌ 选词失败,跳过该seed: {error_msg[:100]}")
+                    break
+
+        if selection_result is None:
+            print(f"      跳过seed: {seed.text}")
+            continue
+
+        print(f"      Agent选择了 {len(selection_result.combinations)} 个组合")
+        print(f"      整体选择思路: {selection_result.overall_reasoning}")
+
+        # 并发评估所有组合的相关度
+        async def evaluate_combination(comb: WordCombination) -> dict:
+            combined = comb.combined_query
+
+            # 验证:组合结果必须包含完整的seed和word
+            # 检查是否包含seed的所有字符
+            seed_chars_in_combined = all(char in combined for char in seed.text)
+            # 检查是否包含word的所有字符
+            word_chars_in_combined = all(char in combined for char in comb.selected_word)
+
+            if not seed_chars_in_combined or not word_chars_in_combined:
+                print(f"        ⚠️  警告:组合不完整")
+                print(f"          Seed: {seed.text}")
+                print(f"          Word: {comb.selected_word}")
+                print(f"          组合: {combined}")
+                print(f"          包含完整seed? {seed_chars_in_combined}")
+                print(f"          包含完整word? {word_chars_in_combined}")
+                # 返回极低分数,让这个组合不会被选中
+                return {
+                    'word': comb.selected_word,
+                    'query': combined,
+                    'score': -1.0,  # 极低分数
+                    'reason': f"组合不完整:缺少seed或word的部分内容",
+                    'reasoning': comb.reasoning
+                }
+
+            # 正常评估,根据轮次选择 prompt
+            score, reason = await evaluate_with_o(combined, o, context.evaluation_cache, round_num=round_num)
+            return {
+                'word': comb.selected_word,
+                'query': combined,
+                'score': score,
+                'reason': reason,
+                'reasoning': comb.reasoning
+            }
+
+        eval_tasks = [evaluate_combination(comb) for comb in selection_result.combinations]
+        top_5 = await asyncio.gather(*eval_tasks)
+
+        print(f"      评估完成,得到 {len(top_5)} 个组合")
+
+        # 将Top 5全部加入q_list_next(去重检查 + 得分过滤)
+        for comb in top_5:
+            # 得分过滤:组合词必须比种子提升至少REQUIRED_SCORE_GAIN才能加入下一轮
+            if comb['score'] < seed.score_with_o + REQUIRED_SCORE_GAIN:
+                print(f"        ⊗ 跳过低分: {comb['query']} (分数{comb['score']:.2f} < 种子{seed.score_with_o:.2f} + {REQUIRED_SCORE_GAIN:.2f})")
+                continue
+
+            # 去重检查
+            if comb['query'] in existing_q_texts:
+                print(f"        ⊗ 跳过重复: {comb['query']}")
+                continue
+
+            print(f"        ✓ {comb['query']} (分数: {comb['score']:.2f} > 种子: {seed.score_with_o:.2f})")
+
+            new_q = Q(
+                text=comb['query'],
+                score_with_o=comb['score'],
+                reason=comb['reason'],
+                from_source="add"
+            )
+            q_list_next.append(new_q)
+            existing_q_texts.add(comb['query'])  # 记录到去重集合
+
+            # 记录已添加的词
+            seed.added_words.append(comb['word'])
+
+        # 保存到add_word_details
+        add_word_details[seed.text] = [
+            {
+                "text": comb['query'],
+                "score": comb['score'],
+                "reason": comb['reason'],
+                "selected_word": comb['word'],
+                "seed_score": seed.score_with_o,  # 添加原始种子的得分
+                "type": "add"
+            }
+            for comb in top_5
+        ]
+
+        # 保存到all_seed_combinations(用于构建seed_list_next)
+        # 附加seed_score,用于后续过滤
+        for comb in top_5:
+            comb['seed_score'] = seed.score_with_o
+        all_seed_combinations.extend(top_5)
+
+    # 4.2 对于sug_list_list中,每个sug大于来自的query分数,加到q_list_next(去重检查)
+    print(f"\n  4.2 将高分sug加入q_list_next...")
+    for sug in all_sugs:
+        # 剪枝检查:跳过来自被剪枝query的sug
+        if sug.from_q and sug.from_q.text in pruned_query_texts:
+            print(f"    ⊗ 跳过来自被剪枝query的sug: {sug.text} (来源: {sug.from_q.text})")
+            continue
+
+        # sug必须比来源query提升至少REQUIRED_SCORE_GAIN才能加入下一轮
+        if sug.from_q and sug.score_with_o >= sug.from_q.score_with_o + REQUIRED_SCORE_GAIN:
+            # 去重检查
+            if sug.text in existing_q_texts:
+                print(f"    ⊗ 跳过重复: {sug.text}")
+                continue
+
+            new_q = Q(
+                text=sug.text,
+                score_with_o=sug.score_with_o,
+                reason=sug.reason,
+                from_source="sug"
+            )
+            q_list_next.append(new_q)
+            existing_q_texts.add(sug.text)  # 记录到去重集合
+            print(f"    ✓ {sug.text} (分数: {sug.score_with_o:.2f} >= 来源query: {sug.from_q.score_with_o:.2f} + {REQUIRED_SCORE_GAIN:.2f})")
+
+    # 5. 构建seed_list_next(关键修改:不保留上一轮的seed)
+    print(f"\n[步骤5] 构建seed_list_next(不保留上轮seed)...")
+    seed_list_next = []
+    existing_seed_texts = set()
+
+    # 5.1 加入本轮所有组合词(只加入得分提升的)
+    print(f"  5.1 加入本轮所有组合词(得分过滤)...")
+    for comb in all_seed_combinations:
+        # 得分过滤:组合词必须比种子提升至少REQUIRED_SCORE_GAIN才作为下一轮种子
+        seed_score = comb.get('seed_score', 0)
+        if comb['score'] < seed_score + REQUIRED_SCORE_GAIN:
+            print(f"    ⊗ 跳过低分: {comb['query']} (分数{comb['score']:.2f} < 种子{seed_score:.2f} + {REQUIRED_SCORE_GAIN:.2f})")
+            continue
+
+        if comb['query'] not in existing_seed_texts:
+            new_seed = Seed(
+                text=comb['query'],
+                added_words=[],  # 新seed的added_words清空
+                from_type="add",
+                score_with_o=comb['score']
+            )
+            seed_list_next.append(new_seed)
+            existing_seed_texts.add(comb['query'])
+            print(f"    ✓ {comb['query']} (分数: {comb['score']:.2f} >= 种子: {seed_score:.2f} + {REQUIRED_SCORE_GAIN:.2f})")
+
+    # 5.2 加入高分sug
+    print(f"  5.2 加入高分sug...")
+    for sug in all_sugs:
+        # 剪枝检查:跳过来自被剪枝query的sug
+        if sug.from_q and sug.from_q.text in pruned_query_texts:
+            continue
+
+        # sug必须比来源query提升至少REQUIRED_SCORE_GAIN才作为下一轮种子
+        if sug.from_q and sug.score_with_o >= sug.from_q.score_with_o + REQUIRED_SCORE_GAIN and sug.text not in existing_seed_texts:
+            new_seed = Seed(
+                text=sug.text,
+                added_words=[],
+                from_type="sug",
+                score_with_o=sug.score_with_o
+            )
+            seed_list_next.append(new_seed)
+            existing_seed_texts.add(sug.text)
+            print(f"    ✓ {sug.text} (分数: {sug.score_with_o:.2f} >= 来源query: {sug.from_q.score_with_o:.2f} + {REQUIRED_SCORE_GAIN:.2f})")
+
+    # 序列化搜索结果数据(包含帖子详情)
+    search_results_data = []
+    for search in search_list:
+        search_results_data.append({
+            "text": search.text,
+            "score_with_o": search.score_with_o,
+            "post_list": [
+                {
+                    "note_id": post.note_id,
+                    "note_url": post.note_url,
+                    "title": post.title,
+                    "body_text": post.body_text,
+                    "images": post.images,
+                    "interact_info": post.interact_info
+                }
+                for post in search.post_list
+            ]
+        })
+
+    # 记录本轮数据
+    round_data.update({
+        "sug_count": len(all_sugs),
+        "high_score_sug_count": len(high_score_sugs),
+        "search_count": len(search_list),
+        "total_posts": sum(len(s.post_list) for s in search_list),
+        "q_list_next_size": len(q_list_next),
+        "seed_list_next_size": len(seed_list_next),
+        "total_combinations": len(all_seed_combinations),
+        "pruned_query_count": len(pruned_query_texts),
+        "pruned_queries": list(pruned_query_texts),
+        "output_q_list": [{"text": q.text, "score": q.score_with_o, "reason": q.reason, "from": q.from_source, "type": "query"} for q in q_list_next],
+        "seed_list_next": [{"text": seed.text, "from": seed.from_type, "score": seed.score_with_o} for seed in seed_list_next],
+        "sug_details": sug_details,
+        "add_word_details": add_word_details,
+        "search_results": search_results_data
+    })
+    context.rounds.append(round_data)
+
+    print(f"\n本轮总结:")
+    print(f"  建议词数量: {len(all_sugs)}")
+    print(f"  高分建议词: {len(high_score_sugs)}")
+    print(f"  搜索数量: {len(search_list)}")
+    print(f"  帖子总数: {sum(len(s.post_list) for s in search_list)}")
+    print(f"  组合词数量: {len(all_seed_combinations)}")
+    print(f"  下轮q数量: {len(q_list_next)}")
+    print(f"  下轮seed数量: {len(seed_list_next)}")
+
+    return q_list_next, seed_list_next, search_list
+
+
+async def iterative_loop(
+    context: RunContext,
+    max_rounds: int = 2,
+    sug_threshold: float = 0.7
+):
+    """主迭代循环"""
+
+    print(f"\n{'='*60}")
+    print(f"开始迭代循环")
+    print(f"最大轮数: {max_rounds}")
+    print(f"sug阈值: {sug_threshold}")
+    print(f"{'='*60}")
+
+    # 初始化
+    seg_list, word_list_1, q_list, seed_list = await initialize(context.o, context)
+
+    # API实例
+    xiaohongshu_api = XiaohongshuSearchRecommendations()
+    xiaohongshu_search = XiaohongshuSearch()
+
+    # 保存初始化数据
+    context.rounds.append({
+        "round_num": 0,
+        "type": "initialization",
+        "seg_list": [{"text": s.text, "score": s.score_with_o, "reason": s.reason, "type": "seg"} for s in seg_list],
+        "word_list_1": [{"text": w.text, "score": w.score_with_o} for w in word_list_1],
+        "q_list_1": [{"text": q.text, "score": q.score_with_o, "reason": q.reason, "type": "query"} for q in q_list],
+        "seed_list": [{"text": s.text, "from_type": s.from_type, "score": s.score_with_o, "type": "seed"} for s in seed_list]
+    })
+
+    # 收集所有搜索结果
+    all_search_list = []
+
+    # 迭代
+    round_num = 1
+    while q_list and round_num <= max_rounds:
+        q_list, seed_list, search_list = await run_round(
+            round_num=round_num,
+            q_list=q_list,
+            word_list_1=word_list_1,  # 传递固定词库
+            seed_list=seed_list,
+            o=context.o,
+            context=context,
+            xiaohongshu_api=xiaohongshu_api,
+            xiaohongshu_search=xiaohongshu_search,
+            sug_threshold=sug_threshold
+        )
+
+        all_search_list.extend(search_list)
+        round_num += 1
+
+    print(f"\n{'='*60}")
+    print(f"迭代完成")
+    print(f"  总轮数: {round_num - 1}")
+    print(f"  总搜索次数: {len(all_search_list)}")
+    print(f"  总帖子数: {sum(len(s.post_list) for s in all_search_list)}")
+    print(f"{'='*60}")
+
+    return all_search_list
+
+
+# ============================================================================
+# 主函数
+# ============================================================================
+
+async def main(input_dir: str, max_rounds: int = 2, sug_threshold: float = 0.7, visualize: bool = False):
+    """主函数"""
+    current_time, log_url = set_trace()
+
+    # 读取输入
+    input_context_file = os.path.join(input_dir, 'context.md')
+    input_q_file = os.path.join(input_dir, 'q.md')
+
+    c = read_file_as_string(input_context_file)  # 原始需求
+    o = read_file_as_string(input_q_file)  # 原始问题
+
+    # 版本信息
+    version = os.path.basename(__file__)
+    version_name = os.path.splitext(version)[0]
+
+    # 日志目录
+    log_dir = os.path.join(input_dir, "output", version_name, current_time)
+
+    # 创建运行上下文
+    run_context = RunContext(
+        version=version,
+        input_files={
+            "input_dir": input_dir,
+            "context_file": input_context_file,
+            "q_file": input_q_file,
+        },
+        c=c,
+        o=o,
+        log_dir=log_dir,
+        log_url=log_url,
+    )
+
+    # 创建日志目录
+    os.makedirs(run_context.log_dir, exist_ok=True)
+
+    # 配置日志文件
+    log_file_path = os.path.join(run_context.log_dir, "run.log")
+    log_file = open(log_file_path, 'w', encoding='utf-8')
+
+    # 重定向stdout到TeeLogger(同时输出到控制台和文件)
+    original_stdout = sys.stdout
+    sys.stdout = TeeLogger(original_stdout, log_file)
+
+    try:
+        print(f"📝 日志文件: {log_file_path}")
+        print(f"{'='*60}\n")
+
+        # 执行迭代
+        all_search_list = await iterative_loop(
+            run_context,
+            max_rounds=max_rounds,
+            sug_threshold=sug_threshold
+        )
+
+        # 格式化输出
+        output = f"原始需求:{run_context.c}\n"
+        output += f"原始问题:{run_context.o}\n"
+        output += f"总搜索次数:{len(all_search_list)}\n"
+        output += f"总帖子数:{sum(len(s.post_list) for s in all_search_list)}\n"
+        output += "\n" + "="*60 + "\n"
+
+        if all_search_list:
+            output += "【搜索结果】\n\n"
+            for idx, search in enumerate(all_search_list, 1):
+                output += f"{idx}. 搜索词: {search.text} (分数: {search.score_with_o:.2f})\n"
+                output += f"   帖子数: {len(search.post_list)}\n"
+                if search.post_list:
+                    for post_idx, post in enumerate(search.post_list[:3], 1):  # 只显示前3个
+                        output += f"   {post_idx}) {post.title}\n"
+                        output += f"      URL: {post.note_url}\n"
+                output += "\n"
+        else:
+            output += "未找到搜索结果\n"
+
+        run_context.final_output = output
+
+        print(f"\n{'='*60}")
+        print("最终结果")
+        print(f"{'='*60}")
+        print(output)
+
+        # 保存上下文文件
+        context_file_path = os.path.join(run_context.log_dir, "run_context.json")
+        context_dict = run_context.model_dump()
+        with open(context_file_path, "w", encoding="utf-8") as f:
+            json.dump(context_dict, f, ensure_ascii=False, indent=2)
+        print(f"\nRunContext saved to: {context_file_path}")
+
+        # 保存详细的搜索结果
+        search_results_path = os.path.join(run_context.log_dir, "search_results.json")
+        search_results_data = [s.model_dump() for s in all_search_list]
+        with open(search_results_path, "w", encoding="utf-8") as f:
+            json.dump(search_results_data, f, ensure_ascii=False, indent=2)
+        print(f"Search results saved to: {search_results_path}")
+
+        # 可视化
+        if visualize:
+            import subprocess
+            output_html = os.path.join(run_context.log_dir, "visualization.html")
+            print(f"\n🎨 生成可视化HTML...")
+
+            # 获取绝对路径
+            abs_context_file = os.path.abspath(context_file_path)
+            abs_output_html = os.path.abspath(output_html)
+
+            # 运行可视化脚本
+            result = subprocess.run([
+                "node",
+                "visualization/sug_v6_1_2_8/index.js",
+                abs_context_file,
+                abs_output_html
+            ])
+
+            if result.returncode == 0:
+                print(f"✅ 可视化已生成: {output_html}")
+            else:
+                print(f"❌ 可视化生成失败")
+
+    finally:
+        # 恢复stdout
+        sys.stdout = original_stdout
+        log_file.close()
+        print(f"\n📝 运行日志已保存: {log_file_path}")
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description="搜索query优化工具 - v6.1.2.115 广度遍历版")
+    parser.add_argument(
+        "--input-dir",
+        type=str,
+        default="input/旅游-逸趣玩旅行/如何获取能体现川西秋季特色的高质量风光摄影素材?",
+        help="输入目录路径,默认: input/旅游-逸趣玩旅行/如何获取能体现川西秋季特色的高质量风光摄影素材?"
+    )
+    parser.add_argument(
+        "--max-rounds",
+        type=int,
+        default=4,
+        help="最大轮数,默认: 4"
+    )
+    parser.add_argument(
+        "--sug-threshold",
+        type=float,
+        default=0.7,
+        help="suggestion阈值,默认: 0.7"
+    )
+    parser.add_argument(
+        "--visualize",
+        action="store_true",
+        default=True,
+        help="运行完成后自动生成可视化HTML"
+    )
+    args = parser.parse_args()
+
+    asyncio.run(main(args.input_dir, max_rounds=args.max_rounds, sug_threshold=args.sug_threshold, visualize=args.visualize))

+ 3 - 4
sug_v6_1_2_122.py

@@ -14,7 +14,7 @@ from lib.utils import read_file_as_string
 from lib.client import get_model
 MODEL_NAME = "google/gemini-2.5-flash"
 # 得分提升阈值:sug或组合词必须比来源query提升至少此幅度才能进入下一轮
-REQUIRED_SCORE_GAIN = 0.05
+REQUIRED_SCORE_GAIN = 0.02
 from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
 from script.search.xiaohongshu_search import XiaohongshuSearch
 
@@ -502,7 +502,7 @@ category_evaluation_instructions = """
 ## 维度独立性声明
 【严格约束】本评估**仅评估内容主体维度**:
 - **只评估**:名词主体 + 限定词(地域、时间、场景、质量等)
-- **完全忽略**:动作、意图、目的(这些由Prompt1评估)
+- **完全忽略**:动作、意图、目的
 - **评估重点**:内容本身的主题和属性
 
 ---
@@ -713,7 +713,6 @@ extension_word_evaluation_instructions = """
 ```
 IF sug词的词汇属于原始问题的作用域元素(动机/对象/场景):
    → 不是延伸词,是作用域内的词
-   → 由Prompt1和Prompt2评估
 
 IF sug词的词汇不属于原始问题的作用域:
    → 是延伸词
@@ -958,7 +957,7 @@ Sug词条:"川西旅行行程规划"
 # 核心原则总结
 
 1. **严格区分**:作用域内的词 ≠ 延伸词
-2. **同义词/细化词不算延伸**:属于作用域范围的词由Prompt1和2评估
+2. **同义词/细化词不算延伸**:属于作用域范围的词由其他prompt评估
 3. **作用域导向**:评估延伸词是否使sug词条更接近原始问题的完整作用域
 4. **目的导向**:评估延伸词是否促进核心目的达成
 5. **分类明确**:准确判定延伸词类型

+ 2756 - 0
sug_v6_1_2_124.py

@@ -0,0 +1,2756 @@
+import asyncio
+import json
+import os
+import sys
+import argparse
+from datetime import datetime
+from typing import Literal
+
+from agents import Agent, Runner, ModelSettings
+from lib.my_trace import set_trace
+from pydantic import BaseModel, Field
+
+from lib.utils import read_file_as_string
+from lib.client import get_model
+MODEL_NAME = "google/gemini-2.5-flash"
+# 得分提升阈值:sug或组合词必须比来源query提升至少此幅度才能进入下一轮
+REQUIRED_SCORE_GAIN = 0.02
+from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
+from script.search.xiaohongshu_search import XiaohongshuSearch
+
+
+# ============================================================================
+# 日志工具类
+# ============================================================================
+
+class TeeLogger:
+    """同时输出到控制台和日志文件的工具类"""
+    def __init__(self, stdout, log_file):
+        self.stdout = stdout
+        self.log_file = log_file
+
+    def write(self, message):
+        self.stdout.write(message)
+        self.log_file.write(message)
+        self.log_file.flush()  # 实时写入,避免丢失日志
+
+    def flush(self):
+        self.stdout.flush()
+        self.log_file.flush()
+
+
+# ============================================================================
+# 数据模型
+# ============================================================================
+
+class Seg(BaseModel):
+    """分词(旧版)- v120使用"""
+    text: str
+    score_with_o: float = 0.0  # 与原始问题的评分
+    reason: str = ""  # 评分理由
+    from_o: str = ""  # 原始问题
+
+
+# ============================================================================
+# 新架构数据模型 (v121)
+# ============================================================================
+
+class Segment(BaseModel):
+    """语义片段(Round 0语义分段结果)"""
+    text: str  # 片段文本
+    type: str  # 语义类型: 疑问标记/核心动作/修饰短语/中心名词/逻辑连接
+    score_with_o: float = 0.0  # 与原始问题的评分
+    reason: str = ""  # 评分理由
+    from_o: str = ""  # 原始问题
+    words: list[str] = Field(default_factory=list)  # 该片段拆分出的词列表(Round 0拆词结果)
+    word_scores: dict[str, float] = Field(default_factory=dict)  # 词的评分 {word: score}
+    word_reasons: dict[str, str] = Field(default_factory=dict)  # 词的评分理由 {word: reason}
+
+
+class DomainCombination(BaseModel):
+    """域组合(Round N的N域组合结果)"""
+    text: str  # 组合后的文本
+    domains: list[int] = Field(default_factory=list)  # 参与组合的域索引列表(对应segments的索引)
+    type_label: str = ""  # 类型标签,如 [疑问标记+核心动作+中心名词]
+    source_words: list[list[str]] = Field(default_factory=list)  # 来源词列表,每个元素是一个域的词列表,如 [["猫咪"], ["梗图"]]
+    score_with_o: float = 0.0  # 与原始问题的评分
+    reason: str = ""  # 评分理由
+    from_segments: list[str] = Field(default_factory=list)  # 来源segment的文本列表
+    source_word_details: list[dict] = Field(default_factory=list)  # 词及其得分信息 [{"domain_index":0,"segment_type":"","words":[{"text":"","score":0.0}]}]
+    source_scores: list[float] = Field(default_factory=list)  # 来源词的分数列表(扁平化)
+    max_source_score: float | None = None  # 来源词的最高分
+    is_above_source_scores: bool = False  # 组合得分是否超过所有来源词
+
+
+# ============================================================================
+# 旧架构数据模型(保留但不使用)
+# ============================================================================
+
+# class Word(BaseModel):
+#     """词(旧版)- v120使用,v121不再使用"""
+#     text: str
+#     score_with_o: float = 0.0  # 与原始问题的评分
+#     from_o: str = ""  # 原始问题
+
+
+class Word(BaseModel):
+    """词"""
+    text: str
+    score_with_o: float = 0.0  # 与原始问题的评分
+    from_o: str = ""  # 原始问题
+
+
+class QFromQ(BaseModel):
+    """Q来源信息(用于Sug中记录)"""
+    text: str
+    score_with_o: float = 0.0
+
+
+class Q(BaseModel):
+    """查询"""
+    text: str
+    score_with_o: float = 0.0  # 与原始问题的评分
+    reason: str = ""  # 评分理由
+    from_source: str = ""  # v120: seg/sug/add; v121新增: segment/domain_comb/sug
+    type_label: str = ""  # v121新增:域类型标签(仅用于domain_comb来源)
+    domain_index: int = -1  # v121新增:域索引(word来源时有效,-1表示无域)
+    domain_type: str = ""  # v121新增:域类型(word来源时表示所属segment的type,如"中心名词")
+
+
+class Sug(BaseModel):
+    """建议词"""
+    text: str
+    score_with_o: float = 0.0  # 与原始问题的评分
+    reason: str = ""  # 评分理由
+    from_q: QFromQ | None = None  # 来自的q
+
+
+class Seed(BaseModel):
+    """种子(旧版)- v120使用,v121不再使用"""
+    text: str
+    added_words: list[str] = Field(default_factory=list)  # 已经增加的words
+    from_type: str = ""  # seg/sug/add
+    score_with_o: float = 0.0  # 与原始问题的评分
+
+
+class Post(BaseModel):
+    """帖子"""
+    title: str = ""
+    body_text: str = ""
+    type: str = "normal"  # video/normal
+    images: list[str] = Field(default_factory=list)  # 图片url列表,第一张为封面
+    video: str = ""  # 视频url
+    interact_info: dict = Field(default_factory=dict)  # 互动信息
+    note_id: str = ""
+    note_url: str = ""
+
+
+class Search(Sug):
+    """搜索结果(继承Sug)"""
+    post_list: list[Post] = Field(default_factory=list)  # 搜索得到的帖子列表
+
+
+class RunContext(BaseModel):
+    """运行上下文"""
+    version: str
+    input_files: dict[str, str]
+    c: str  # 原始需求
+    o: str  # 原始问题
+    log_url: str
+    log_dir: str
+
+    # v121新增:语义分段结果
+    segments: list[dict] = Field(default_factory=list)  # Round 0的语义分段结果
+
+    # 每轮的数据
+    rounds: list[dict] = Field(default_factory=list)  # 每轮的详细数据
+
+    # 最终结果
+    final_output: str | None = None
+
+    # 评估缓存:避免重复评估相同文本
+    evaluation_cache: dict[str, tuple[float, str]] = Field(default_factory=dict)
+    # key: 文本, value: (score, reason)
+
+
+# ============================================================================
+# Agent 定义
+# ============================================================================
+
+# ============================================================================
+# v121 新增 Agent
+# ============================================================================
+
+# Agent: 语义分段专家 (Prompt1)
+class SemanticSegment(BaseModel):
+    """单个语义片段"""
+    segment_text: str = Field(..., description="片段文本")
+    segment_type: str = Field(..., description="语义类型(疑问标记/核心动作/修饰短语/中心名词/逻辑连接)")
+    reasoning: str = Field(..., description="分段理由")
+
+
+class SemanticSegmentation(BaseModel):
+    """语义分段结果"""
+    segments: list[SemanticSegment] = Field(..., description="语义片段列表")
+    overall_reasoning: str = Field(..., description="整体分段思路")
+
+
+semantic_segmentation_instructions = """
+你是语义分段专家。给定一个搜索query,将其拆分成不同语义类型的片段。
+
+## 语义类型定义
+1. **疑问引导**:如何、怎么、什么、哪里等疑问词
+2. **核心动作**:关键动词,如获取、制作、拍摄、寻找等
+3. **修饰短语**:形容词、副词等修饰成分
+4. **中心名词**:核心名词
+5. **逻辑连接**:并且、或者、以及等连接词(较少出现)
+
+## 分段原则
+1. **语义完整性**:每个片段应该是一个完整的语义单元
+2. **类型互斥**:每个片段只能属于一种类型
+3. **保留原文**:片段文本必须保留原query中的字符,不得改写
+4. **顺序保持**:片段顺序应与原query一致
+
+
+## 输出要求
+- segments: 片段列表
+  - segment_text: 片段文本(必须来自原query)
+  - segment_type: 语义类型(从5种类型中选择)
+  - reasoning: 为什么这样分段
+- overall_reasoning: 整体分段思路
+
+## JSON输出规范
+1. **格式要求**:必须输出标准JSON格式
+2. **引号规范**:字符串中如需表达引用,使用书名号《》或「」,不要使用英文引号或中文引号""
+""".strip()
+
+semantic_segmenter = Agent[None](
+    name="语义分段专家",
+    instructions=semantic_segmentation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=SemanticSegmentation,
+)
+
+
+# ============================================================================
+# v120 保留 Agent
+# ============================================================================
+
+# Agent 1: 分词专家(v121用于Round 0拆词)
+class WordSegmentation(BaseModel):
+    """分词结果"""
+    words: list[str] = Field(..., description="分词结果列表")
+    reasoning: str = Field(..., description="分词理由")
+
+word_segmentation_instructions = """
+你是分词专家。给定一个query,将其拆分成有意义的最小单元。
+
+## 分词原则
+1. 保留有搜索意义的词汇
+2. 拆分成独立的概念
+3. 保留专业术语的完整性
+4. 去除虚词(的、吗、呢等),但保留疑问词(如何、为什么、怎样等)
+
+## 输出要求
+返回分词列表和分词理由。
+""".strip()
+
+word_segmenter = Agent[None](
+    name="分词专家",
+    instructions=word_segmentation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=WordSegmentation,
+)
+
+
+# Agent 2: 动机维度评估专家 + 品类维度评估专家(两阶段评估)
+
+# 动机评估的嵌套模型
+class CoreMotivationExtraction(BaseModel):
+    """核心动机提取"""
+    简要说明核心动机: str = Field(..., description="核心动机说明")
+
+class MotivationEvaluation(BaseModel):
+    """动机维度评估"""
+    原始问题核心动机提取: CoreMotivationExtraction = Field(..., description="原始问题核心动机提取")
+    动机维度得分: float = Field(..., description="动机维度得分 -1~1")
+    简要说明动机维度相关度理由: str = Field(..., description="动机维度相关度理由")
+    得分为零的原因: Literal["原始问题无动机", "sug词条无动机", "动机不匹配", "不适用"] = Field(default="不适用", description="当得分为0时的原因分类")
+
+class CategoryEvaluation(BaseModel):
+    """品类维度评估"""
+    品类维度得分: float = Field(..., description="品类维度得分 -1~1")
+    简要说明品类维度相关度理由: str = Field(..., description="品类维度相关度理由")
+
+class ExtensionWordEvaluation(BaseModel):
+    """延伸词评估"""
+    延伸词得分: float = Field(..., ge=-1, le=1, description="延伸词得分 -1~1")
+    简要说明延伸词维度相关度理由: str = Field(..., description="延伸词维度相关度理由")
+
+# 动机评估 prompt(统一版本)
+motivation_evaluation_instructions = """
+# 角色
+你是**专业的动机意图评估专家**。
+任务:判断<平台sug词条>与<原始问题>的**动机意图匹配度**,给出**-1到1之间**的数值评分。
+
+---
+# 输入信息
+你将接收到以下输入:
+- **<原始问题>**:用户的初始查询问题,代表用户的真实需求意图。
+- **<平台sug词条>**:待评估的词条,可能是单个或多个作用域的组合
+---
+
+
+# 核心约束
+
+## 维度独立性声明
+【严格约束】本评估**仅评估动机意图维度**:
+- **只评估** 用户"想要做什么",即原始问题的行为意图和目的
+- 核心是 **动词**:获取、学习、拍摄、制作、寻找等
+- 包括:核心动作 + 使用场景 + 最终目的
+- **评估重点**:动作本身及其语义方向
+ **禁止使用"主题相关"作为评分依据**:评分理由中不得出现"主题"、"内容"、"话题"等词
+
+---
+
+# 作用域与动作意图
+
+## 什么是作用域?
+**作用域 = 动机层 + 对象层 + 场景层**
+
+## 动作意图的识别
+
+### 方法1: 显性动词直接提取
+
+当原始问题明确包含动词时,直接提取
+示例:
+"如何获取素材" → 核心动机 = "获取"
+"寻找拍摄技巧" → 核心动机 = "寻找"(或"学习")
+"制作视频教程" → 核心动机 = "制作"
+
+### 方法2: 隐性动词语义推理
+当原始问题没有显性动词时,需要结合上下文推理
+
+如果原始问题是纯名词短语,无任何动作线索:
+→ 核心动机 = 无法识别
+→ 在此情况下,动机维度得分应为 0。
+示例:
+"摄影" → 无法识别动机,动机维度得分 = 0
+"川西风光" → 无法识别动机,动机维度得分 = 0
+
+---
+
+# 部分作用域的处理
+
+## 情况1:sug词条是原始问题的部分作用域
+
+当sug词条只包含原始问题的部分作用域时,需要判断:
+1. sug词条是否包含动作意图
+2. 如果包含,动作是否匹配
+
+**示例**:
+```
+原始问题:"川西旅行行程规划"
+- 完整作用域:规划(动作)+ 旅行行程(对象)+ 川西(场景)
+
+Sug词条:"川西旅行"
+- 包含作用域:旅行(部分对象)+ 川西(场景)
+- 缺失作用域:规划(动作)
+- 动作意图评分:0(无动作意图)
+```
+
+**评分原则**:
+- 如果sug词条缺失动机层(动作) → 动作意图得分 = 0
+- 如果sug词条包含动机层 → 按动作匹配度评分
+
+---
+
+# 评分标准
+
+## 【正向匹配】
+
+### +0.9~1.0:核心动作完全一致
+**示例**:
+- "规划旅行行程" vs "安排旅行路线" → 0.98
+  - 规划≈安排,语义完全一致
+- "获取素材" vs "下载素材" → 0.97
+  - 获取≈下载,语义完全一致
+
+- 特殊规则: 如果sug词的核心动作是原始问题动作的**具体化子集**,也判定为完全一致
+例: 原始问题"扣除猫咪主体的方法" vs sug词"扣除猫咪眼睛的方法"(子集但目的一致
+**注意**:此处不考虑对象和场景是否一致,只看动作本身
+
+###+0.75~0.95: 核心动作语义相近或为同义表达
+  - 例: 原始问题"如何获取素材" vs sug词"如何下载素材"
+  - 同义词对: 获取≈下载≈寻找, 技巧≈方法≈教程≈攻略
+
+### +0.50~0.75:动作意图相关
+**判定标准**:
+- 动作是实现原始意图的相关路径
+- 或动作是原始意图的前置/后置步骤
+
+**示例**:
+- "获取素材" vs "管理素材" → 0.65
+  - 管理是获取后的相关步骤
+- "规划行程" vs "预订酒店" → 0.60
+  - 预订是规划的具体实施步骤
+
+### +0.25~0.50:动作意图弱相关
+**判定标准**:
+- 动作在同一大类但方向不同
+- 或动作有间接关联
+
+**示例**:
+- "学习摄影技巧" vs "欣赏摄影作品" → 0.35
+  - 都与摄影有关,但学习≠欣赏
+- "规划旅行" vs "回忆旅行" → 0.30
+  - 都与旅行有关,但方向不同
+
+---
+
+## 【中性/无关】
+
+### 0:无动作意图或动作完全无关
+**适用场景**:
+1. 原始问题或sug词条无法识别动作
+2. 两者动作意图完全无关
+
+**示例**:
+- "如何获取素材" vs "摄影器材" → 0
+  - sug词条无动作意图
+- "川西风光" vs "风光摄影作品" → 0
+  - 原始问题无动作意图
+
+**理由模板**:
+- "sug词条无明确动作意图,无法评估动作匹配度"
+- "原始问题无明确动作意图,动作维度得分为0"
+
+---
+
+## 【负向偏离】
+
+### -0.2~-0.05:动作方向轻度偏离
+**示例**:
+- "学习摄影技巧" vs "销售摄影课程" → -0.10
+  - 学习 vs 销售,方向有偏差
+
+### -0.5~-0.25:动作意图明显冲突
+**示例**:
+- "获取免费素材" vs "购买素材" → -0.35
+  - 获取免费 vs 购买,明显冲突
+
+### -1.0~-0.55:动作意图完全相反
+**示例**:
+- "下载素材" vs "上传素材" → -0.70
+  - 下载 vs 上传,方向完全相反
+
+---
+
+## 得分为零的原因(语义判断)
+
+当动机维度得分为 0 时,需要在 `得分为零的原因` 字段中选择以下之一:
+- **"原始问题无动机"**:原始问题是纯名词短语,无法识别任何动作意图
+- **"sug词条无动机"**:sug词条中不包含任何动作意图
+- **"动机不匹配"**:双方都有动作,但完全无关联
+- **"不适用"**:得分不为零时使用此默认值
+
+---
+
+# 输出格式
+输出结果必须为一个 **JSON 格式**,包含以下内容:
+```json
+{
+  "原始问题核心动机提取": {
+    "简要说明核心动机": ""
+  },
+  "动机维度得分": "-1到1之间的小数",
+  "简要说明动机维度相关度理由": "评估该sug词条与原始问题动机匹配程度的理由,包含作用域覆盖情况",
+  "得分为零的原因": "原始问题无动机/sug词条无动机/动机不匹配/不适用"
+}
+```
+
+**输出约束(非常重要)**:
+1. **字符串长度限制**:\"简要说明动机维度相关度理由\"字段必须控制在**150字以内**
+2. **JSON格式规范**:必须生成完整的JSON格式,确保字符串用双引号包裹且正确闭合
+3. **引号使用**:字符串中如需表达引用,请使用《》或「」代替单引号或双引号
+
+---
+
+# 核心原则总结
+1. **只评估动作**:完全聚焦于动作意图,不管对象和场景
+2. **作用域识别**:识别作用域但只评估动机层
+3. **严格标准一致性**:对所有用例使用相同的评估标准,避免评分飘移
+4. **理由纯粹**:评分理由只能谈动作,不能谈对象、场景、主题
+""".strip()
+
+# 品类评估 prompt
+category_evaluation_instructions = """
+# 角色
+你是**专业的内容主体评估专家**。
+任务:判断<平台sug词条>与<原始问题>的**内容主体匹配度**,给出**-1到1之间**的数值评分。
+
+---
+
+# 输入信息
+- **<原始问题>**:用户的完整需求描述
+- **<平台sug词条>**:待评估的词条,可能是单个或多个作用域的组合
+---
+
+
+# 核心约束
+
+## 维度独立性声明
+【严格约束】本评估**仅评估内容主体维度**:
+- **只评估**:名词主体 + 限定词(地域、时间、场景、质量等)
+- **完全忽略**:动作、意图、目的
+- **评估重点**:内容本身的主题和属性
+
+---
+
+# 作用域与内容主体
+
+## 什么是作用域?
+**作用域 = 动机层 + 对象层 + 场景层**
+
+在Prompt2中:
+- **动机层(动作)完全忽略**
+- **只评估对象层 + 场景层(限定词)**
+
+## 内容主体的构成
+
+**内容主体 = 核心名词 + 限定词**
+
+
+---
+
+# 作用域覆盖度评估
+
+## 核心原则:越完整越高分
+
+**完整性公式**:
+```
+作用域覆盖度 = sug词条包含的作用域元素 / 原始问题的作用域元素总数
+```
+
+**评分影响**:
+- 覆盖度100% → 基础高分(0.9+)
+- 覆盖度50-99% → 中高分(0.6-0.9)
+- 覆盖度<50% → 中低分(0.3-0.6)
+- 覆盖度=0 → 低分或0分
+
+---
+
+## 部分作用域的处理
+
+### 情况1:sug词条包含原始问题的所有对象层和场景层元素
+**评分**:0.95-1.0
+
+**示例**:
+```
+原始问题:"川西秋季风光摄影素材"
+- 对象层:摄影素材
+- 场景层:川西 + 秋季 + 风光
+
+Sug词条:"川西秋季风光摄影作品"
+- 对象层:摄影作品(≈素材)
+- 场景层:川西 + 秋季 + 风光
+- 覆盖度:100%
+- 评分:0.98
+```
+
+### 情况2:sug词条包含部分场景层元素
+**评分**:根据覆盖比例
+
+**示例**:
+```
+原始问题:"川西秋季风光摄影素材"
+- 对象层:摄影素材
+- 场景层:川西 + 秋季 + 风光(3个元素)
+
+Sug词条:"川西风光摄影素材"
+- 对象层:摄影素材 ✓
+- 场景层:川西 + 风光(2个元素)
+- 覆盖度:(1+2)/(1+3) = 75%
+- 评分:0.85
+```
+
+### 情况3:sug词条只包含对象层,无场景层
+**评分**:根据对象匹配度和覆盖度
+
+**示例**:
+```
+原始问题:"川西秋季风光摄影素材"
+- 对象层:摄影素材
+- 场景层:川西 + 秋季 + 风光
+
+Sug词条:"摄影素材"
+- 对象层:摄影素材 ✓
+- 场景层:无
+- 覆盖度:1/4 = 25%
+- 评分:0.50(对象匹配但缺失所有限定)
+```
+
+### 情况4:sug词条只包含场景层,无对象层
+**评分**:较低分
+
+**示例**:
+```
+原始问题:"川西旅行行程规划"
+- 对象层:旅行行程
+- 场景层:川西
+
+Sug词条:"川西"
+- 对象层:无
+- 场景层:川西 ✓
+- 覆盖度:1/2 = 50%
+- 评分:0.35(只有场景,缺失核心对象)
+```
+
+---
+
+# 评估核心原则
+
+## 原则1:只看表面词汇,禁止联想推演
+**严格约束**:只能基于sug词实际包含的词汇评分
+
+**错误案例**:
+- ❌ "川西旅行" vs "旅行"
+  - 错误:"旅行可以包括川西,所以有关联" → 评分0.7
+  - 正确:"sug词只有'旅行',无'川西',缺失地域限定" → 评分0.50
+
+
+---
+
+# 评分标准
+
+## 【正向匹配】
+
++0.95~1.0: 核心主体+所有关键限定词完全匹配
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"川西秋季风光摄影作品"
+
++0.75~0.95: 核心主体匹配,存在限定词匹配
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"川西风光摄影素材"(缺失"秋季")
+
++0.5~0.75: 核心主体匹配,无限定词匹配或合理泛化
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"四川风光摄影"
+
++0.3~0.5: 核心主体匹配,但限定词缺失或存在语义错位
+  - 特别注意"语义身份"差异,主体词出现但上下文语义不同
+  - 例:
+    · "猫咪的XX行为"(猫咪是行为者)
+    · vs "用猫咪表达XX的梗图"(猫咪是媒介)
+    · 虽都含"猫咪+XX",但语义角色不同
+
++0.2~0.3: 主体词不匹配,限定词缺失或错位
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"风光摄影入门"
+
++0.05~0.2: 主体词过度泛化或仅抽象相似
+  - 例: sug词是通用概念,原始问题是特定概念
+    sug词"每日计划"(通用)vs 原始问题 "川西旅行行程"(特定)
+      → 评分:0.08
+
+【中性/无关】
+0: 类别明显不同,没有明确目的,无明确关联
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"人像摄影素材"
+  - 例: 原始问题无法识别动机 且 sug词也无明确动作 → 0
+
+【负向偏离】
+-0.2~-0.05: 主体词或限定词存在误导性
+  - 例: 原始问题"免费摄影素材" vs sug词"付费摄影素材库"
+
+-0.5~-0.25: 主体词明显错位或品类冲突
+  - 例: 原始问题"风光摄影素材" vs sug词"人像修图教程"
+
+-1.0~-0.55: 完全错误的品类或有害引导
+  - 例: 原始问题"正版素材获取" vs sug词"盗版素材下载"
+
+
+---
+
+# 输出格式
+输出结果必须为一个 **JSON 格式**,包含以下内容:
+```json
+{
+  "品类维度得分": "-1到1之间的小数",
+  "简要说明品类维度相关度理由": "评估该sug词条与原始问题品类匹配程度的理由,包含作用域覆盖理由"
+}
+```
+
+**输出约束(非常重要)**:
+1. **字符串长度限制**:\"简要说明品类维度相关度理由\"字段必须控制在**150字以内**
+2. **JSON格式规范**:必须生成完整的JSON格式,确保字符串用双引号包裹且正确闭合
+3. **引号使用**:字符串中如需表达引用,请使用《》或「」代替单引号或双引号
+
+---
+
+# 核心原则总结
+
+1. **只看名词和限定词**:完全忽略动作和意图
+2. **作用域覆盖优先**:覆盖的作用域元素越多,分数越高
+3. **禁止联想推演**:只看sug词实际包含的词汇
+4. **通用≠特定**:通用概念不等于特定概念
+5. **理由纯粹**:评分理由只能谈对象、限定词、覆盖度
+""".strip()
+
+# 延伸词评估 prompt
+extension_word_evaluation_instructions = """
+# 角色
+你是**专业的延伸词语义评估专家**。
+任务:识别<平台sug词条>中的延伸词,评估其对原始问题作用域的补全度和目的贡献度,给出**-1到1之间**的数值评分。
+
+---
+# 输入信息
+- **<原始问题>**:用户的完整需求描述
+- **<平台sug词条>**:待评估的词条,可能是单个或多个作用域的组合
+---
+
+# 核心概念
+
+## 什么是延伸词?
+**延伸词**:<平台sug词条>中出现,但不属于<原始问题>作用域范围内的词汇或概念
+
+**关键判断**:
+```
+IF sug词的词汇属于原始问题的作用域元素(动机/对象/场景):
+   → 不是延伸词,是作用域内的词
+
+IF sug词的词汇不属于原始问题的作用域:
+   → 是延伸词
+   → 由Prompt3评估
+```
+
+---
+
+# 作用域与延伸词
+
+## 作用域
+**作用域 = 动机层 + 对象层 + 场景层**
+
+**非延伸词示例**(属于作用域内):
+```
+原始问题:"川西旅行行程规划"
+作用域:
+- 动机层:规划
+- 对象层:旅行行程
+- 场景层:川西
+
+Sug词条:"川西旅行行程规划攻略"
+- "川西"→ 属于场景层,不是延伸词
+- "旅行"→ 属于对象层,不是延伸词
+- "行程"→ 属于对象层,不是延伸词
+- "规划"→ 属于动机层,不是延伸词
+- "攻略"→ 与"规划"同义,不是延伸词
+- 结论:无延伸词
+```
+
+**延伸词示例**(不属于作用域):
+```
+原始问题:"川西旅行行程规划"
+作用域:规划 + 旅行行程 + 川西
+
+Sug词条:"川西旅行行程规划住宿推荐"
+- "住宿推荐"→ 不属于原始问题任何作用域
+- 结论:延伸词 = ["住宿推荐"]
+```
+
+---
+
+# 延伸词识别方法
+
+## 步骤1:提取原始问题的作用域元素
+```
+动机层:提取动作及其同义词
+对象层:提取核心名词及其同义词
+场景层:提取所有限定词
+```
+
+## 步骤2:提取sug词条的所有关键词
+```
+提取sug词条中的所有实词(名词、动词、形容词)
+```
+
+## 步骤3:匹配判定
+```
+FOR 每个sug词条关键词:
+   IF 该词 ∈ 原始问题作用域元素(包括同义词):
+      → 不是延伸词
+   ELSE:
+      → 是延伸词
+```
+
+## 步骤4:同义词/相近词判定规则
+
+### 不算延伸词的情况:
+**同义词**:
+- 行程 ≈ 路线 ≈ 安排 ≈ 计划
+- 获取 ≈ 下载 ≈ 寻找 ≈ 收集
+- 技巧 ≈ 方法 ≈ 教程 ≈ 攻略
+- 素材 ≈ 资源 ≈ 作品 ≈ 内容
+
+**具体化/细化**:
+- 原始:"川西旅游" + sug词:"稻城亚丁"(川西的具体地点)→ 不算延伸
+- 原始:"摄影技巧" + sug词:"风光摄影"(摄影的细化)→ 不算延伸
+- 原始:"素材" + sug词:"高清素材"(素材的质量细化)→ 不算延伸
+
+**判定逻辑**:
+```
+IF sug词的概念是原始问题概念的子集/下位词/同义词:
+   → 不算延伸词
+   → 视为对原问题的细化或重述
+```
+
+---
+
+### 算延伸词的情况:
+
+**新增维度**:原始问题未涉及的信息维度
+- 原始:"川西旅行" + sug词:"住宿" → 延伸词
+- 原始:"摄影素材" + sug词:"版权" → 延伸词
+
+**新增限定条件**:原始问题未提及的约束
+- 原始:"素材获取" + sug词:"免费" → 延伸词
+- 原始:"旅行行程" + sug词:"7天" → 延伸词
+
+**扩展主题**:相关但非原问题范围
+- 原始:"川西行程" + sug词:"美食推荐" → 延伸词
+- 原始:"摄影技巧" + sug词:"后期修图" → 延伸词
+
+**工具/方法**:原始问题未提及的具体工具
+- 原始:"视频剪辑" + sug词:"PR软件" → 延伸词
+- 原始:"图片处理" + sug词:"PS教程" → 延伸词
+
+---
+
+# 延伸词类型与评分
+
+## 核心评估维度:对原始问题作用域的贡献
+
+### 维度1:作用域补全度
+延伸词是否帮助sug词条更接近原始问题的完整作用域?
+
+
+### 维度2:目的达成度
+延伸词是否促进原始问题核心目的的达成?
+---
+####类型1:作用域增强型
+**定义**:延伸词是原始问题核心目的,或补全关键作用域
+**得分范围**:+0.12~+0.20
+
+**判定标准**:
+- 使sug词条更接近原始问题的完整需求
+---
+
+####类型2:作用域辅助型
+**定义**:延伸词对核心目的有辅助作用,但非必需
+
+**得分范围**:+0.05~+0.12
+
+**判定标准**:
+- sug词条更丰富但不改变原始需求核心
+
+---
+
+####类型3:作用域无关型
+**定义**:延伸词与核心目的无实质关联
+
+**得分**:0
+
+**示例**:
+- 原始:"如何拍摄风光" + 延伸词:"相机品牌排行"
+  - 评分:0
+  - 理由:品牌排行与拍摄技巧无关
+
+---
+
+####类型4:作用域稀释型(轻度负向)
+**定义**:延伸词稀释原始问题的聚焦度,降低内容针对性
+
+**得分范围**:-0.08~-0.18
+
+**判定标准**:
+- 引入无关信息,分散注意力
+- 降低内容的专注度和深度
+- 使sug词条偏离原始问题的核心
+
+**示例**:
+- 原始:"专业风光摄影技巧" + 延伸词:"手机拍照"
+  - 评分:-0.12
+  - 理由:手机拍照与专业摄影需求不符,稀释专业度
+
+- 原始:"川西深度游攻略" + 延伸词:"周边一日游"
+  - 评分:-0.10
+  - 理由:一日游与深度游定位冲突,稀释深度
+
+
+---
+
+# 特殊情况处理
+
+## 情况1:多个延伸词同时存在
+**处理方法**:分别评估每个延伸词,然后综合
+
+**综合规则**:
+```
+延伸词总得分 = Σ(每个延伸词得分) / 延伸词数量
+
+考虑累积效应:
+- 多个增强型延伸词 → 总分可能超过单个最高分,但上限+0.25
+- 正负延伸词并存 → 相互抵消
+- 多个冲突型延伸词 → 总分下限-0.60
+```
+
+**示例**:
+```
+原始:"川西旅行行程"
+Sug词条:"川西旅行行程住宿美食推荐"
+延伸词识别:
+- "住宿推荐"→ 增强型,+0.18
+- "美食推荐"→ 辅助型,+0.10
+总得分:(0.18 + 0.10) / 2 = 0.14
+```
+
+---
+
+## 情况2:无延伸词
+**处理方法**:
+```
+IF sug词条无延伸词:
+   延伸词得分 = 0
+   理由:"sug词条未引入延伸词,所有词汇均属于原始问题作用域范围"
+```
+
+---
+
+## 情况3:延伸词使sug词条更接近原始问题
+**特殊加成**:
+```
+IF 延伸词是原始问题隐含需求的显式化:
+   → 额外加成 +0.05
+```
+
+**示例**:
+```
+原始:"川西旅行" (隐含需要行程规划)
+Sug词条:"川西旅行行程规划"
+- "行程规划"可能被识别为延伸词,但它显式化了隐含需求
+- 给予额外加成
+```
+
+---
+
+# 输出格式
+输出结果必须为一个 **JSON 格式**,包含以下内容:
+```json
+{
+  "延伸词得分": "-1到1之间的小数",
+  "简要说明延伸词维度相关度理由": "评估延伸词对作用域的影响"
+}
+```
+
+**输出约束(非常重要)**:
+1. **字符串长度限制**:\"简要说明延伸词维度相关度理由\"字段必须控制在**150字以内**
+2. **JSON格式规范**:必须生成完整的JSON格式,确保字符串用双引号包裹且正确闭合
+3. **引号使用**:字符串中如需表达引用,请使用《》或「」代替单引号或双引号
+
+---
+
+# 核心原则总结
+
+1. **严格区分**:作用域内的词 ≠ 延伸词
+2. **同义词/细化词不算延伸**:属于作用域范围的词由其他prompt评估
+3. **作用域导向**:评估延伸词是否使sug词条更接近原始问题的完整作用域
+4. **目的导向**:评估延伸词是否促进核心目的达成
+5. **分类明确**:准确判定延伸词类型
+6. **理由充分**:每个延伸词都要说明其对作用域和目的的影响
+7. **谨慎负分**:仅在明确冲突或有害时使用负分
+""".strip()
+
+# 创建评估 Agent
+motivation_evaluator = Agent[None](
+    name="动机维度评估专家(后续轮次)",
+    instructions=motivation_evaluation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=MotivationEvaluation)
+
+category_evaluator = Agent[None](
+    name="品类维度评估专家",
+    instructions=category_evaluation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=CategoryEvaluation
+)
+
+extension_word_evaluator = Agent[None](
+    name="延伸词评估专家",
+    instructions=extension_word_evaluation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=ExtensionWordEvaluation,
+    model_settings=ModelSettings(temperature=0.2)
+)
+
+
+# ============================================================================
+# v120 保留但不使用的 Agent(v121不再使用)
+# ============================================================================
+
+# # Agent 3: 加词选择专家(旧版 - v120使用,v121不再使用)
+# class WordCombination(BaseModel):
+#     """单个词组合"""
+#     selected_word: str = Field(..., description="选择的词")
+#     combined_query: str = Field(..., description="组合后的新query")
+#     reasoning: str = Field(..., description="选择理由")
+
+# class WordSelectionTop5(BaseModel):
+#     """加词选择结果(Top 5)"""
+#     combinations: list[WordCombination] = Field(
+#         ...,
+#         description="选择的Top 5组合(不足5个则返回所有)",
+#         min_items=1,
+#         max_items=5
+#     )
+#     overall_reasoning: str = Field(..., description="整体选择思路")
+
+# word_selection_instructions 已删除 (v121不再使用)
+
+# word_selector = Agent[None](
+#     name="加词组合专家",
+#     instructions=word_selection_instructions,
+#     model=get_model(MODEL_NAME),
+#     output_type=WordSelectionTop5,
+#     model_settings=ModelSettings(temperature=0.2),
+# )
+
+
+# ============================================================================
+# 辅助函数
+# ============================================================================
+
+# ============================================================================
+# v121 新增辅助函数
+# ============================================================================
+
+def get_ordered_subsets(words: list[str], min_len: int = 1) -> list[list[str]]:
+    """
+    生成words的所有有序子集(可跳过但不可重排)
+
+    使用 itertools.combinations 生成索引组合,保持原始顺序
+
+    Args:
+        words: 词列表
+        min_len: 子集最小长度
+
+    Returns:
+        所有可能的有序子集列表
+
+    Example:
+        words = ["川西", "秋季", "风光"]
+        结果:
+        - 长度1: ["川西"], ["秋季"], ["风光"]
+        - 长度2: ["川西", "秋季"], ["川西", "风光"], ["秋季", "风光"]
+        - 长度3: ["川西", "秋季", "风光"]
+        共 C(3,1) + C(3,2) + C(3,3) = 3 + 3 + 1 = 7种
+    """
+    from itertools import combinations
+
+    subsets = []
+    n = len(words)
+
+    # 遍历所有可能的长度(从min_len到n)
+    for r in range(min_len, n + 1):
+        # 生成长度为r的所有索引组合
+        for indices in combinations(range(n), r):
+            # 按照原始顺序提取词
+            subset = [words[i] for i in indices]
+            subsets.append(subset)
+
+    return subsets
+
+
+def generate_domain_combinations(segments: list[Segment], n_domains: int) -> list[DomainCombination]:
+    """
+    生成N域组合
+
+    步骤:
+    1. 从len(segments)个域中选择n_domains个域(组合,保持顺序)
+    2. 对每个选中的域,生成其words的所有有序子集
+    3. 计算笛卡尔积,生成所有可能的组合
+
+    Args:
+        segments: 语义片段列表
+        n_domains: 参与组合的域数量
+
+    Returns:
+        所有可能的N域组合列表
+
+    Example:
+        有4个域: [疑问标记, 核心动作, 修饰短语, 中心名词]
+        n_domains=2时,选择域的方式: C(4,2) = 6种
+
+        假设选中[核心动作, 中心名词]:
+        - 核心动作的words: ["获取"], 子集: ["获取"]
+        - 中心名词的words: ["风光", "摄影", "素材"], 子集: 7种
+        则该域选择下的组合数: 1 * 7 = 7种
+    """
+    from itertools import combinations, product
+
+    all_combinations = []
+    n = len(segments)
+
+    # 检查参数有效性
+    if n_domains > n or n_domains < 1:
+        return []
+
+    # 1. 选择n_domains个域(保持原始顺序)
+    for domain_indices in combinations(range(n), n_domains):
+        selected_segments = [segments[i] for i in domain_indices]
+
+        # 新增:如果所有域都只有1个词,跳过(单段落单词不组合)
+        if all(len(seg.words) == 1 for seg in selected_segments):
+            continue
+
+        # 2. 为每个选中的域生成其words的所有有序子集
+        domain_subsets = []
+        for seg in selected_segments:
+            if len(seg.words) == 0:
+                # 如果某个域没有词,跳过该域组合
+                domain_subsets = []
+                break
+            subsets = get_ordered_subsets(seg.words, min_len=1)
+            domain_subsets.append(subsets)
+
+        # 如果某个域没有词,跳过
+        if len(domain_subsets) != n_domains:
+            continue
+
+        # 3. 计算笛卡尔积
+        for word_combination in product(*domain_subsets):
+            # word_combination 是一个tuple,每个元素是一个词列表
+            # 例如: (["获取"], ["风光", "摄影"])
+
+            # 计算总词数
+            total_words = sum(len(words) for words in word_combination)
+
+            # 如果总词数<=1,跳过(组词必须大于1个词)
+            if total_words <= 1:
+                continue
+
+            # 将所有词连接成一个字符串
+            combined_text = "".join(["".join(words) for words in word_combination])
+
+            # 生成类型标签
+            type_labels = [selected_segments[i].type for i in range(n_domains)]
+            type_label = "[" + "+".join(type_labels) + "]"
+
+            # 创建DomainCombination对象
+            comb = DomainCombination(
+                text=combined_text,
+                domains=list(domain_indices),
+                type_label=type_label,
+                source_words=[list(words) for words in word_combination],  # 保存来源词
+                from_segments=[seg.text for seg in selected_segments]
+            )
+            all_combinations.append(comb)
+
+    return all_combinations
+
+
+def extract_words_from_segments(segments: list[Segment]) -> list[Q]:
+    """
+    从 segments 中提取所有 words,转换为 Q 对象列表
+
+    用于 Round 1 的输入:将 Round 0 的 words 转换为可用于请求SUG的 query 列表
+
+    Args:
+        segments: Round 0 的语义片段列表
+
+    Returns:
+        list[Q]: word 列表,每个 word 作为一个 Q 对象
+    """
+    q_list = []
+
+    for seg_idx, segment in enumerate(segments):
+        for word in segment.words:
+            # 从 segment.word_scores 获取该 word 的评分
+            word_score = segment.word_scores.get(word, 0.0)
+            word_reason = segment.word_reasons.get(word, "")
+
+            # 创建 Q 对象
+            q = Q(
+                text=word,
+                score_with_o=word_score,
+                reason=word_reason,
+                from_source="word",  # 标记来源为 word
+                type_label=f"[{segment.type}]",  # 保留域信息
+                domain_index=seg_idx,  # 添加域索引
+                domain_type=segment.type  # 添加域类型(如"中心名词"、"核心动作")
+            )
+            q_list.append(q)
+
+    return q_list
+
+
+# ============================================================================
+# v120 保留辅助函数
+# ============================================================================
+
+def calculate_final_score(
+    motivation_score: float,
+    category_score: float,
+    extension_score: float,
+    zero_reason: str,
+    extension_reason: str = ""
+) -> tuple[float, str]:
+    """
+    三维评估综合打分
+
+    实现动态权重分配:
+    - 情况1:标准情况 → 动机50% + 品类40% + 延伸词10%
+    - 情况2:原始问题无动机 → 品类70% + 延伸词30%
+    - 情况3:sug词条无动机 → 品类80% + 延伸词20%
+    - 情况4:无延伸词 → 动机70% + 品类30%
+    - 规则3:负分传导 → 核心维度严重负向时上限为0
+    - 规则4:完美匹配加成 → 双维度≥0.95时加成+0.10
+
+    Args:
+        motivation_score: 动机维度得分 -1~1
+        category_score: 品类维度得分 -1~1
+        extension_score: 延伸词得分 -1~1
+        zero_reason: 当motivation_score=0时的原因
+        extension_reason: 延伸词评估理由,用于判断是否无延伸词
+
+    Returns:
+        (最终得分, 规则说明)
+    """
+
+    # 情况2:原始问题无动作意图
+    if motivation_score == 0 and zero_reason == "原始问题无动机":
+        W1, W2, W3 = 0.0, 0.70, 0.30
+        base_score = category_score * W2 + extension_score * W3
+        rule_applied = "情况2:原始问题无动作意图,权重调整为 品类70% + 延伸词30%"
+
+    # 情况3:sug词条无动作意图(但原始问题有)
+    elif motivation_score == 0 and zero_reason == "sug词条无动机":
+        W1, W2, W3 = 0.0, 0.80, 0.20
+        base_score = category_score * W2 + extension_score * W3
+        rule_applied = "情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%"
+
+    # 情况4:无延伸词
+    elif extension_score == 0:
+        W1, W2, W3 = 0.70, 0.30, 0.0
+        base_score = motivation_score * W1 + category_score * W2
+        rule_applied = "情况4:无延伸词,权重调整为 动机70% + 品类30%"
+
+    else:
+        # 情况1:标准权重
+        W1, W2, W3 = 0.50, 0.40, 0.10
+        base_score = motivation_score * W1 + category_score * W2 + extension_score * W3
+        rule_applied = ""
+
+    # 规则4:完美匹配加成
+    if motivation_score >= 0.95 and category_score >= 0.95:
+        base_score += 0.10
+        rule_applied += (" + " if rule_applied else "") + "规则4:双维度完美匹配,加成+0.10"
+
+    # 规则3:负分传导
+    if motivation_score <= -0.5 or category_score <= -0.5:
+        base_score = min(base_score, 0)
+        rule_applied += (" + " if rule_applied else "") + "规则3:核心维度严重负向,上限=0"
+
+    # 边界处理
+    final_score = max(-1.0, min(1.0, base_score))
+
+    return final_score, rule_applied
+
+
+def clean_json_string(text: str) -> str:
+    """清理JSON中的非法控制字符(保留 \t \n \r)"""
+    import re
+    # 移除除了 \t(09) \n(0A) \r(0D) 之外的所有控制字符
+    return re.sub(r'[\x00-\x08\x0B\x0C\x0E-\x1F]', '', text)
+
+
+def process_note_data(note: dict) -> Post:
+    """处理搜索接口返回的帖子数据"""
+    note_card = note.get("note_card", {})
+    image_list = note_card.get("image_list", [])
+    interact_info = note_card.get("interact_info", {})
+    user_info = note_card.get("user", {})
+
+    # ========== 调试日志 START ==========
+    note_id = note.get("id", "")
+    raw_title = note_card.get("display_title")  # 不提供默认值
+    raw_body = note_card.get("desc")
+    raw_type = note_card.get("type")
+
+    # 打印原始值类型和内容
+    print(f"\n[DEBUG] 处理帖子 {note_id}:")
+    print(f"  raw_title 类型: {type(raw_title).__name__}, 值: {repr(raw_title)}")
+    print(f"  raw_body 类型: {type(raw_body).__name__}, 值: {repr(raw_body)[:100] if raw_body else repr(raw_body)}")
+    print(f"  raw_type 类型: {type(raw_type).__name__}, 值: {repr(raw_type)}")
+
+    # 检查是否为 None
+    if raw_title is None:
+        print(f"  ⚠️  WARNING: display_title 是 None!")
+    if raw_body is None:
+        print(f"  ⚠️  WARNING: desc 是 None!")
+    if raw_type is None:
+        print(f"  ⚠️  WARNING: type 是 None!")
+    # ========== 调试日志 END ==========
+
+    # 提取图片URL - 使用新的字段名 image_url
+    images = []
+    for img in image_list:
+        if isinstance(img, dict):
+            # 尝试新字段名 image_url,如果不存在则尝试旧字段名 url_default
+            img_url = img.get("image_url") or img.get("url_default")
+            if img_url:
+                images.append(img_url)
+
+    # 判断类型
+    note_type = note_card.get("type", "normal")
+    video_url = ""
+    if note_type == "video":
+        video_info = note_card.get("video", {})
+        if isinstance(video_info, dict):
+            # 尝试获取视频URL
+            video_url = video_info.get("media", {}).get("stream", {}).get("h264", [{}])[0].get("master_url", "")
+
+    return Post(
+        note_id=note.get("id") or "",
+        title=note_card.get("display_title") or "",
+        body_text=note_card.get("desc") or "",
+        type=note_type,
+        images=images,
+        video=video_url,
+        interact_info={
+            "liked_count": interact_info.get("liked_count", 0),
+            "collected_count": interact_info.get("collected_count", 0),
+            "comment_count": interact_info.get("comment_count", 0),
+            "shared_count": interact_info.get("shared_count", 0)
+        },
+        note_url=f"https://www.xiaohongshu.com/explore/{note.get('id', '')}"
+    )
+
+
+async def evaluate_with_o(text: str, o: str, cache: dict[str, tuple[float, str]] | None = None) -> tuple[float, str]:
+    """评估文本与原始问题o的相关度
+
+    采用两阶段评估 + 代码计算规则:
+    1. 动机维度评估(权重70%)
+    2. 品类维度评估(权重30%)
+    3. 应用规则A/B/C调整得分
+
+    Args:
+        text: 待评估的文本
+        o: 原始问题
+        cache: 评估缓存(可选),用于避免重复评估
+
+    Returns:
+        tuple[float, str]: (最终相关度分数, 综合评估理由)
+    """
+    # 检查缓存
+    if cache is not None and text in cache:
+        cached_score, cached_reason = cache[text]
+        print(f"  ⚡ 缓存命中: {text} -> {cached_score:.2f}")
+        return cached_score, cached_reason
+
+    # 准备输入
+    eval_input = f"""
+<原始问题>
+{o}
+</原始问题>
+
+<平台sug词条>
+{text}
+</平台sug词条>
+
+请评估平台sug词条与原始问题的匹配度。
+"""
+
+    # 添加重试机制
+    max_retries = 2
+    last_error = None
+
+    for attempt in range(max_retries):
+        try:
+            # 并发调用三个评估器
+            motivation_task = Runner.run(motivation_evaluator, eval_input)
+            category_task = Runner.run(category_evaluator, eval_input)
+            extension_task = Runner.run(extension_word_evaluator, eval_input)
+
+            motivation_result, category_result, extension_result = await asyncio.gather(
+                motivation_task,
+                category_task,
+                extension_task
+            )
+
+            # 获取评估结果
+            motivation_eval: MotivationEvaluation = motivation_result.final_output
+            category_eval: CategoryEvaluation = category_result.final_output
+            extension_eval: ExtensionWordEvaluation = extension_result.final_output
+
+            # 提取得分
+            motivation_score = motivation_eval.动机维度得分
+            category_score = category_eval.品类维度得分
+            extension_score = extension_eval.延伸词得分
+            zero_reason = motivation_eval.得分为零的原因
+
+            # 应用规则计算最终得分
+            final_score, rule_applied = calculate_final_score(
+                motivation_score, category_score, extension_score, zero_reason,
+                extension_eval.简要说明延伸词维度相关度理由
+            )
+
+            # 组合评估理由
+            core_motivation = motivation_eval.原始问题核心动机提取.简要说明核心动机
+            motivation_reason = motivation_eval.简要说明动机维度相关度理由
+            category_reason = category_eval.简要说明品类维度相关度理由
+            extension_reason = extension_eval.简要说明延伸词维度相关度理由
+
+            combined_reason = (
+                f"【核心动机】{core_motivation}\n"
+                f"【动机维度 {motivation_score:.2f}】{motivation_reason}\n"
+                f"【品类维度 {category_score:.2f}】{category_reason}\n"
+                f"【延伸词维度 {extension_score:.2f}】{extension_reason}\n"
+                f"【最终得分 {final_score:.2f}】"
+            )
+
+            # 添加规则说明
+            if rule_applied:
+                combined_reason += f"\n【规则说明】{rule_applied}"
+
+            # 存入缓存
+            if cache is not None:
+                cache[text] = (final_score, combined_reason)
+
+            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
+
+
+# ============================================================================
+# 核心流程函数
+# ============================================================================
+
+async def initialize(o: str, context: RunContext) -> tuple[list[Seg], list[Word], list[Q], list[Seed]]:
+    """
+    初始化阶段
+
+    Returns:
+        (seg_list, word_list_1, q_list_1, seed_list)
+    """
+    print(f"\n{'='*60}")
+    print(f"初始化阶段")
+    print(f"{'='*60}")
+
+    # 1. 分词:原始问题(o) ->分词-> seg_list
+    print(f"\n[步骤1] 分词...")
+    result = await Runner.run(word_segmenter, o)
+    segmentation: WordSegmentation = result.final_output
+
+    seg_list = []
+    for word in segmentation.words:
+        seg_list.append(Seg(text=word, from_o=o))
+
+    print(f"分词结果: {[s.text for s in seg_list]}")
+    print(f"分词理由: {segmentation.reasoning}")
+
+    # 2. 分词评估:seg_list -> 每个seg与o进行评分(使用信号量限制并发数)
+    print(f"\n[步骤2] 评估每个分词与原始问题的相关度...")
+
+    MAX_CONCURRENT_SEG_EVALUATIONS = 10
+    seg_semaphore = asyncio.Semaphore(MAX_CONCURRENT_SEG_EVALUATIONS)
+
+    async def evaluate_seg(seg: Seg) -> Seg:
+        async with seg_semaphore:
+            # 初始化阶段的分词评估使用第一轮 prompt (round_num=1)
+            seg.score_with_o, seg.reason = await evaluate_with_o(seg.text, o, context.evaluation_cache, round_num=1)
+            return seg
+
+    if seg_list:
+        print(f"  开始评估 {len(seg_list)} 个分词(并发限制: {MAX_CONCURRENT_SEG_EVALUATIONS})...")
+        eval_tasks = [evaluate_seg(seg) for seg in seg_list]
+        await asyncio.gather(*eval_tasks)
+
+    for seg in seg_list:
+        print(f"  {seg.text}: {seg.score_with_o:.2f}")
+
+    # 3. 构建word_list_1: seg_list -> word_list_1(固定词库)
+    print(f"\n[步骤3] 构建word_list_1(固定词库)...")
+    word_list_1 = []
+    for seg in seg_list:
+        word_list_1.append(Word(
+            text=seg.text,
+            score_with_o=seg.score_with_o,
+            from_o=o
+        ))
+    print(f"word_list_1(固定): {[w.text for w in word_list_1]}")
+
+    # 4. 构建q_list_1:seg_list 作为 q_list_1
+    print(f"\n[步骤4] 构建q_list_1...")
+    q_list_1 = []
+    for seg in seg_list:
+        q_list_1.append(Q(
+            text=seg.text,
+            score_with_o=seg.score_with_o,
+            reason=seg.reason,
+            from_source="seg"
+        ))
+    print(f"q_list_1: {[q.text for q in q_list_1]}")
+
+    # 5. 构建seed_list: seg_list -> seed_list
+    print(f"\n[步骤5] 构建seed_list...")
+    seed_list = []
+    for seg in seg_list:
+        seed_list.append(Seed(
+            text=seg.text,
+            added_words=[],
+            from_type="seg",
+            score_with_o=seg.score_with_o
+        ))
+    print(f"seed_list: {[s.text for s in seed_list]}")
+
+    return seg_list, word_list_1, q_list_1, seed_list
+
+
+async def run_round(
+    round_num: int,
+    q_list: list[Q],
+    word_list_1: list[Word],
+    seed_list: list[Seed],
+    o: str,
+    context: RunContext,
+    xiaohongshu_api: XiaohongshuSearchRecommendations,
+    xiaohongshu_search: XiaohongshuSearch,
+    sug_threshold: float = 0.7
+) -> tuple[list[Q], list[Seed], list[Search]]:
+    """
+    运行一轮
+
+    Args:
+        round_num: 轮次编号
+        q_list: 当前轮的q列表
+        word_list_1: 固定的词库(第0轮分词结果)
+        seed_list: 当前的seed列表
+        o: 原始问题
+        context: 运行上下文
+        xiaohongshu_api: 建议词API
+        xiaohongshu_search: 搜索API
+        sug_threshold: suggestion的阈值
+
+    Returns:
+        (q_list_next, seed_list_next, search_list)
+    """
+    print(f"\n{'='*60}")
+    print(f"第{round_num}轮")
+    print(f"{'='*60}")
+
+    round_data = {
+        "round_num": round_num,
+        "input_q_list": [{"text": q.text, "score": q.score_with_o, "type": "query"} for q in q_list],
+        "input_word_list_1_size": len(word_list_1),
+        "input_seed_list_size": len(seed_list)
+    }
+
+    # 1. 请求sug:q_list -> 每个q请求sug接口 -> sug_list_list
+    print(f"\n[步骤1] 为每个q请求建议词...")
+    sug_list_list = []  # list of list
+    for q in q_list:
+        print(f"\n  处理q: {q.text}")
+        suggestions = xiaohongshu_api.get_recommendations(keyword=q.text)
+
+        q_sug_list = []
+        if suggestions:
+            print(f"    获取到 {len(suggestions)} 个建议词")
+            for sug_text in suggestions:
+                sug = Sug(
+                    text=sug_text,
+                    from_q=QFromQ(text=q.text, score_with_o=q.score_with_o)
+                )
+                q_sug_list.append(sug)
+        else:
+            print(f"    未获取到建议词")
+
+        sug_list_list.append(q_sug_list)
+
+    # 2. sug评估:sug_list_list -> 每个sug与o进行评分(并发)
+    print(f"\n[步骤2] 评估每个建议词与原始问题的相关度...")
+
+    # 2.1 收集所有需要评估的sug,并记录它们所属的q
+    all_sugs = []
+    sug_to_q_map = {}  # 记录每个sug属于哪个q
+    for i, q_sug_list in enumerate(sug_list_list):
+        if q_sug_list:
+            q_text = q_list[i].text
+            for sug in q_sug_list:
+                all_sugs.append(sug)
+                sug_to_q_map[id(sug)] = q_text
+
+    # 2.2 并发评估所有sug(使用信号量限制并发数)
+    # 每个 evaluate_sug 内部会并发调用 2 个 LLM,所以这里限制为 5,实际并发 LLM 请求为 10
+    MAX_CONCURRENT_EVALUATIONS = 5
+    semaphore = asyncio.Semaphore(MAX_CONCURRENT_EVALUATIONS)
+
+    async def evaluate_sug(sug: Sug) -> Sug:
+        async with semaphore:  # 限制并发数
+            # 根据轮次选择 prompt: 第一轮使用 round1 prompt,后续使用标准 prompt
+            sug.score_with_o, sug.reason = await evaluate_with_o(sug.text, o, context.evaluation_cache, round_num=round_num)
+            return sug
+
+    if all_sugs:
+        print(f"  开始评估 {len(all_sugs)} 个建议词(并发限制: {MAX_CONCURRENT_EVALUATIONS})...")
+        eval_tasks = [evaluate_sug(sug) for sug in all_sugs]
+        await asyncio.gather(*eval_tasks)
+
+    # 2.3 打印结果并组织到sug_details
+    sug_details = {}  # 保存每个Q对应的sug列表
+    for i, q_sug_list in enumerate(sug_list_list):
+        if q_sug_list:
+            q_text = q_list[i].text
+            print(f"\n  来自q '{q_text}' 的建议词:")
+            sug_details[q_text] = []
+            for sug in q_sug_list:
+                print(f"    {sug.text}: {sug.score_with_o:.2f}")
+                # 保存到sug_details
+                sug_details[q_text].append({
+                    "text": sug.text,
+                    "score": sug.score_with_o,
+                    "reason": sug.reason,
+                    "type": "sug"
+                })
+
+    # 2.4 剪枝判断(已禁用 - 保留所有分支)
+    pruned_query_texts = set()
+    if False:  # 原: if round_num >= 2:  # 剪枝功能已禁用,保留代码以便后续调整
+        print(f"\n[剪枝判断] 第{round_num}轮开始应用剪枝策略...")
+        for i, q in enumerate(q_list):
+            q_sug_list = sug_list_list[i]
+
+            if len(q_sug_list) == 0:
+                continue  # 没有sug则不剪枝
+
+            # 剪枝条件1: 所有sug分数都低于query分数
+            all_lower_than_query = all(sug.score_with_o < q.score_with_o for sug in q_sug_list)
+            # 剪枝条件2: 所有sug分数都低于0.5
+            all_below_threshold = all(sug.score_with_o < 0.5 for sug in q_sug_list)
+
+            if all_lower_than_query and all_below_threshold:
+                pruned_query_texts.add(q.text)
+                max_sug_score = max(sug.score_with_o for sug in q_sug_list)
+                print(f"  🔪 剪枝: {q.text} (query分数:{q.score_with_o:.2f}, sug最高分:{max_sug_score:.2f}, 全部<0.5)")
+
+        if pruned_query_texts:
+            print(f"  本轮共剪枝 {len(pruned_query_texts)} 个query")
+        else:
+            print(f"  本轮无query被剪枝")
+    else:
+        print(f"\n[剪枝判断] 剪枝功能已禁用,保留所有分支")
+
+    # 3. search_list构建
+    print(f"\n[步骤3] 构建search_list(阈值>{sug_threshold})...")
+    search_list = []
+    high_score_sugs = [sug for sug in all_sugs if sug.score_with_o > sug_threshold]
+
+    if high_score_sugs:
+        print(f"  找到 {len(high_score_sugs)} 个高分建议词")
+
+        # 并发搜索
+        async def search_for_sug(sug: Sug) -> Search:
+            print(f"    搜索: {sug.text}")
+            try:
+                search_result = xiaohongshu_search.search(keyword=sug.text)
+                result_str = search_result.get("result", "{}")
+                if isinstance(result_str, str):
+                    result_data = json.loads(result_str)
+                else:
+                    result_data = result_str
+
+                notes = result_data.get("data", {}).get("data", [])
+                post_list = []
+                for note in notes[:10]:  # 只取前10个
+                    post = process_note_data(note)
+                    post_list.append(post)
+
+                print(f"      → 找到 {len(post_list)} 个帖子")
+
+                return Search(
+                    text=sug.text,
+                    score_with_o=sug.score_with_o,
+                    from_q=sug.from_q,
+                    post_list=post_list
+                )
+            except Exception as e:
+                print(f"      ✗ 搜索失败: {e}")
+                return Search(
+                    text=sug.text,
+                    score_with_o=sug.score_with_o,
+                    from_q=sug.from_q,
+                    post_list=[]
+                )
+
+        search_tasks = [search_for_sug(sug) for sug in high_score_sugs]
+        search_list = await asyncio.gather(*search_tasks)
+    else:
+        print(f"  没有高分建议词,search_list为空")
+
+    # 4. 构建q_list_next
+    print(f"\n[步骤4] 构建q_list_next...")
+    q_list_next = []
+    existing_q_texts = set()  # 用于去重
+    add_word_details = {}  # 保存每个seed对应的组合词列表
+    all_seed_combinations = []  # 保存本轮所有seed的组合词(用于后续构建seed_list_next)
+
+    # 4.1 对于seed_list中的每个seed,从word_list_1中选词组合,产生Top 5
+    print(f"\n  4.1 为每个seed加词(产生Top 5组合)...")
+    for seed in seed_list:
+        print(f"\n    处理seed: {seed.text}")
+
+        # 剪枝检查:跳过被剪枝的seed
+        if seed.text in pruned_query_texts:
+            print(f"      ⊗ 跳过被剪枝的seed: {seed.text}")
+            continue
+
+        # 从固定词库word_list_1筛选候选词
+        candidate_words = []
+        for word in word_list_1:
+            # 检查词是否已在seed中
+            if word.text in seed.text:
+                continue
+            # 检查词是否已被添加过
+            if word.text in seed.added_words:
+                continue
+            candidate_words.append(word)
+
+        if not candidate_words:
+            print(f"      没有可用的候选词")
+            continue
+
+        print(f"      候选词数量: {len(candidate_words)}")
+
+        # 调用Agent一次性选择并组合Top 5(添加重试机制)
+        candidate_words_text = ', '.join([w.text for w in candidate_words])
+        selection_input = f"""
+<原始问题>
+{o}
+</原始问题>
+
+<当前Seed>
+{seed.text}
+</当前Seed>
+
+<候选词列表>
+{candidate_words_text}
+</候选词列表>
+
+请从候选词列表中选择最多5个最合适的词,分别与当前seed组合成新的query。
+"""
+
+        # 重试机制
+        max_retries = 2
+        selection_result = None
+        for attempt in range(max_retries):
+            try:
+                result = await Runner.run(word_selector, selection_input)
+                selection_result = result.final_output
+                break  # 成功则跳出
+            except Exception as e:
+                error_msg = str(e)
+                if attempt < max_retries - 1:
+                    print(f"      ⚠️  选词失败 (尝试 {attempt+1}/{max_retries}): {error_msg[:100]}")
+                    await asyncio.sleep(1)
+                else:
+                    print(f"      ❌ 选词失败,跳过该seed: {error_msg[:100]}")
+                    break
+
+        if selection_result is None:
+            print(f"      跳过seed: {seed.text}")
+            continue
+
+        print(f"      Agent选择了 {len(selection_result.combinations)} 个组合")
+        print(f"      整体选择思路: {selection_result.overall_reasoning}")
+
+        # 并发评估所有组合的相关度
+        async def evaluate_combination(comb: WordCombination) -> dict:
+            combined = comb.combined_query
+
+            # 验证:组合结果必须包含完整的seed和word
+            # 检查是否包含seed的所有字符
+            seed_chars_in_combined = all(char in combined for char in seed.text)
+            # 检查是否包含word的所有字符
+            word_chars_in_combined = all(char in combined for char in comb.selected_word)
+
+            if not seed_chars_in_combined or not word_chars_in_combined:
+                print(f"        ⚠️  警告:组合不完整")
+                print(f"          Seed: {seed.text}")
+                print(f"          Word: {comb.selected_word}")
+                print(f"          组合: {combined}")
+                print(f"          包含完整seed? {seed_chars_in_combined}")
+                print(f"          包含完整word? {word_chars_in_combined}")
+                # 返回极低分数,让这个组合不会被选中
+                return {
+                    'word': comb.selected_word,
+                    'query': combined,
+                    'score': -1.0,  # 极低分数
+                    'reason': f"组合不完整:缺少seed或word的部分内容",
+                    'reasoning': comb.reasoning
+                }
+
+            # 正常评估,根据轮次选择 prompt
+            score, reason = await evaluate_with_o(combined, o, context.evaluation_cache, round_num=round_num)
+            return {
+                'word': comb.selected_word,
+                'query': combined,
+                'score': score,
+                'reason': reason,
+                'reasoning': comb.reasoning
+            }
+
+        eval_tasks = [evaluate_combination(comb) for comb in selection_result.combinations]
+        top_5 = await asyncio.gather(*eval_tasks)
+
+        print(f"      评估完成,得到 {len(top_5)} 个组合")
+
+        # 将Top 5全部加入q_list_next(去重检查 + 得分过滤)
+        for comb in top_5:
+            # 得分过滤:组合词必须比种子提升至少REQUIRED_SCORE_GAIN才能加入下一轮
+            if comb['score'] < seed.score_with_o + REQUIRED_SCORE_GAIN:
+                print(f"        ⊗ 跳过低分: {comb['query']} (分数{comb['score']:.2f} < 种子{seed.score_with_o:.2f} + {REQUIRED_SCORE_GAIN:.2f})")
+                continue
+
+            # 去重检查
+            if comb['query'] in existing_q_texts:
+                print(f"        ⊗ 跳过重复: {comb['query']}")
+                continue
+
+            print(f"        ✓ {comb['query']} (分数: {comb['score']:.2f} > 种子: {seed.score_with_o:.2f})")
+
+            new_q = Q(
+                text=comb['query'],
+                score_with_o=comb['score'],
+                reason=comb['reason'],
+                from_source="add"
+            )
+            q_list_next.append(new_q)
+            existing_q_texts.add(comb['query'])  # 记录到去重集合
+
+            # 记录已添加的词
+            seed.added_words.append(comb['word'])
+
+        # 保存到add_word_details
+        add_word_details[seed.text] = [
+            {
+                "text": comb['query'],
+                "score": comb['score'],
+                "reason": comb['reason'],
+                "selected_word": comb['word'],
+                "seed_score": seed.score_with_o,  # 添加原始种子的得分
+                "type": "add"
+            }
+            for comb in top_5
+        ]
+
+        # 保存到all_seed_combinations(用于构建seed_list_next)
+        # 附加seed_score,用于后续过滤
+        for comb in top_5:
+            comb['seed_score'] = seed.score_with_o
+        all_seed_combinations.extend(top_5)
+
+    # 4.2 对于sug_list_list中,每个sug大于来自的query分数,加到q_list_next(去重检查)
+    print(f"\n  4.2 将高分sug加入q_list_next...")
+    for sug in all_sugs:
+        # 剪枝检查:跳过来自被剪枝query的sug
+        if sug.from_q and sug.from_q.text in pruned_query_texts:
+            print(f"    ⊗ 跳过来自被剪枝query的sug: {sug.text} (来源: {sug.from_q.text})")
+            continue
+
+        # sug必须比来源query提升至少REQUIRED_SCORE_GAIN才能加入下一轮
+        if sug.from_q and sug.score_with_o >= sug.from_q.score_with_o + REQUIRED_SCORE_GAIN:
+            # 去重检查
+            if sug.text in existing_q_texts:
+                print(f"    ⊗ 跳过重复: {sug.text}")
+                continue
+
+            new_q = Q(
+                text=sug.text,
+                score_with_o=sug.score_with_o,
+                reason=sug.reason,
+                from_source="sug"
+            )
+            q_list_next.append(new_q)
+            existing_q_texts.add(sug.text)  # 记录到去重集合
+            print(f"    ✓ {sug.text} (分数: {sug.score_with_o:.2f} >= 来源query: {sug.from_q.score_with_o:.2f} + {REQUIRED_SCORE_GAIN:.2f})")
+
+    # 5. 构建seed_list_next(关键修改:不保留上一轮的seed)
+    print(f"\n[步骤5] 构建seed_list_next(不保留上轮seed)...")
+    seed_list_next = []
+    existing_seed_texts = set()
+
+    # 5.1 加入本轮所有组合词(只加入得分提升的)
+    print(f"  5.1 加入本轮所有组合词(得分过滤)...")
+    for comb in all_seed_combinations:
+        # 得分过滤:组合词必须比种子提升至少REQUIRED_SCORE_GAIN才作为下一轮种子
+        seed_score = comb.get('seed_score', 0)
+        if comb['score'] < seed_score + REQUIRED_SCORE_GAIN:
+            print(f"    ⊗ 跳过低分: {comb['query']} (分数{comb['score']:.2f} < 种子{seed_score:.2f} + {REQUIRED_SCORE_GAIN:.2f})")
+            continue
+
+        if comb['query'] not in existing_seed_texts:
+            new_seed = Seed(
+                text=comb['query'],
+                added_words=[],  # 新seed的added_words清空
+                from_type="add",
+                score_with_o=comb['score']
+            )
+            seed_list_next.append(new_seed)
+            existing_seed_texts.add(comb['query'])
+            print(f"    ✓ {comb['query']} (分数: {comb['score']:.2f} >= 种子: {seed_score:.2f} + {REQUIRED_SCORE_GAIN:.2f})")
+
+    # 5.2 加入高分sug
+    print(f"  5.2 加入高分sug...")
+    for sug in all_sugs:
+        # 剪枝检查:跳过来自被剪枝query的sug
+        if sug.from_q and sug.from_q.text in pruned_query_texts:
+            continue
+
+        # sug必须比来源query提升至少REQUIRED_SCORE_GAIN才作为下一轮种子
+        if sug.from_q and sug.score_with_o >= sug.from_q.score_with_o + REQUIRED_SCORE_GAIN and sug.text not in existing_seed_texts:
+            new_seed = Seed(
+                text=sug.text,
+                added_words=[],
+                from_type="sug",
+                score_with_o=sug.score_with_o
+            )
+            seed_list_next.append(new_seed)
+            existing_seed_texts.add(sug.text)
+            print(f"    ✓ {sug.text} (分数: {sug.score_with_o:.2f} >= 来源query: {sug.from_q.score_with_o:.2f} + {REQUIRED_SCORE_GAIN:.2f})")
+
+    # 序列化搜索结果数据(包含帖子详情)
+    search_results_data = []
+    for search in search_list:
+        search_results_data.append({
+            "text": search.text,
+            "score_with_o": search.score_with_o,
+            "post_list": [
+                {
+                    "note_id": post.note_id,
+                    "note_url": post.note_url,
+                    "title": post.title,
+                    "body_text": post.body_text,
+                    "images": post.images,
+                    "interact_info": post.interact_info
+                }
+                for post in search.post_list
+            ]
+        })
+
+    # 记录本轮数据
+    round_data.update({
+        "sug_count": len(all_sugs),
+        "high_score_sug_count": len(high_score_sugs),
+        "search_count": len(search_list),
+        "total_posts": sum(len(s.post_list) for s in search_list),
+        "q_list_next_size": len(q_list_next),
+        "seed_list_next_size": len(seed_list_next),
+        "total_combinations": len(all_seed_combinations),
+        "pruned_query_count": len(pruned_query_texts),
+        "pruned_queries": list(pruned_query_texts),
+        "output_q_list": [{"text": q.text, "score": q.score_with_o, "reason": q.reason, "from": q.from_source, "type": "query"} for q in q_list_next],
+        "seed_list_next": [{"text": seed.text, "from": seed.from_type, "score": seed.score_with_o} for seed in seed_list_next],
+        "sug_details": sug_details,
+        "add_word_details": add_word_details,
+        "search_results": search_results_data
+    })
+    context.rounds.append(round_data)
+
+    print(f"\n本轮总结:")
+    print(f"  建议词数量: {len(all_sugs)}")
+    print(f"  高分建议词: {len(high_score_sugs)}")
+    print(f"  搜索数量: {len(search_list)}")
+    print(f"  帖子总数: {sum(len(s.post_list) for s in search_list)}")
+    print(f"  组合词数量: {len(all_seed_combinations)}")
+    print(f"  下轮q数量: {len(q_list_next)}")
+    print(f"  下轮seed数量: {len(seed_list_next)}")
+
+    return q_list_next, seed_list_next, search_list
+
+
+async def iterative_loop(
+    context: RunContext,
+    max_rounds: int = 2,
+    sug_threshold: float = 0.7
+):
+    """主迭代循环"""
+
+    print(f"\n{'='*60}")
+    print(f"开始迭代循环")
+    print(f"最大轮数: {max_rounds}")
+    print(f"sug阈值: {sug_threshold}")
+    print(f"{'='*60}")
+
+    # 初始化
+    seg_list, word_list_1, q_list, seed_list = await initialize(context.o, context)
+
+    # API实例
+    xiaohongshu_api = XiaohongshuSearchRecommendations()
+    xiaohongshu_search = XiaohongshuSearch()
+
+    # 保存初始化数据
+    context.rounds.append({
+        "round_num": 0,
+        "type": "initialization",
+        "seg_list": [{"text": s.text, "score": s.score_with_o, "reason": s.reason, "type": "seg"} for s in seg_list],
+        "word_list_1": [{"text": w.text, "score": w.score_with_o} for w in word_list_1],
+        "q_list_1": [{"text": q.text, "score": q.score_with_o, "reason": q.reason, "type": "query"} for q in q_list],
+        "seed_list": [{"text": s.text, "from_type": s.from_type, "score": s.score_with_o, "type": "seed"} for s in seed_list]
+    })
+
+    # 收集所有搜索结果
+    all_search_list = []
+
+    # 迭代
+    round_num = 1
+    while q_list and round_num <= max_rounds:
+        q_list, seed_list, search_list = await run_round(
+            round_num=round_num,
+            q_list=q_list,
+            word_list_1=word_list_1,  # 传递固定词库
+            seed_list=seed_list,
+            o=context.o,
+            context=context,
+            xiaohongshu_api=xiaohongshu_api,
+            xiaohongshu_search=xiaohongshu_search,
+            sug_threshold=sug_threshold
+        )
+
+        all_search_list.extend(search_list)
+        round_num += 1
+
+    print(f"\n{'='*60}")
+    print(f"迭代完成")
+    print(f"  总轮数: {round_num - 1}")
+    print(f"  总搜索次数: {len(all_search_list)}")
+    print(f"  总帖子数: {sum(len(s.post_list) for s in all_search_list)}")
+    print(f"{'='*60}")
+
+    return all_search_list
+
+
+# ============================================================================
+# v121 新架构核心流程函数
+# ============================================================================
+
+async def initialize_v2(o: str, context: RunContext) -> list[Segment]:
+    """
+    v121 Round 0 初始化阶段
+
+    流程:
+    1. 语义分段: 调用 semantic_segmenter 将原始问题拆分成语义片段
+    2. 拆词: 对每个segment调用 word_segmenter 进行拆词
+    3. 评估: 对每个segment和词进行评估
+    4. 不进行组合(Round 0只分段和拆词)
+
+    Returns:
+        语义片段列表 (Segment)
+    """
+    print(f"\n{'='*60}")
+    print(f"Round 0: 初始化阶段(语义分段 + 拆词)")
+    print(f"{'='*60}")
+
+    # 1. 语义分段
+    print(f"\n[步骤1] 语义分段...")
+    result = await Runner.run(semantic_segmenter, o)
+    segmentation: SemanticSegmentation = result.final_output
+
+    print(f"语义分段结果: {len(segmentation.segments)} 个片段")
+    print(f"整体分段思路: {segmentation.overall_reasoning}")
+
+    segment_list = []
+    for seg_item in segmentation.segments:
+        segment = Segment(
+            text=seg_item.segment_text,
+            type=seg_item.segment_type,
+            from_o=o
+        )
+        segment_list.append(segment)
+        print(f"  - [{segment.type}] {segment.text}")
+
+    # 2. 对每个segment拆词并评估
+    print(f"\n[步骤2] 对每个segment拆词并评估...")
+
+    MAX_CONCURRENT_EVALUATIONS = 5
+    semaphore = asyncio.Semaphore(MAX_CONCURRENT_EVALUATIONS)
+
+    async def process_segment(segment: Segment) -> Segment:
+        """处理单个segment: 拆词 + 评估segment + 评估词"""
+        async with semaphore:
+            # 2.1 拆词
+            word_result = await Runner.run(word_segmenter, segment.text)
+            word_segmentation: WordSegmentation = word_result.final_output
+            segment.words = word_segmentation.words
+
+            # 2.2 评估segment与原始问题的相关度
+            segment.score_with_o, segment.reason = await evaluate_with_o(
+                segment.text, o, context.evaluation_cache
+            )
+
+            # 2.3 评估每个词与原始问题的相关度
+            word_eval_tasks = []
+            for word in segment.words:
+                async def eval_word(w: str) -> tuple[str, float, str]:
+                    score, reason = await evaluate_with_o(w, o, context.evaluation_cache)
+                    return w, score, reason
+                word_eval_tasks.append(eval_word(word))
+
+            word_results = await asyncio.gather(*word_eval_tasks)
+            for word, score, reason in word_results:
+                segment.word_scores[word] = score
+                segment.word_reasons[word] = reason
+
+            return segment
+
+    if segment_list:
+        print(f"  开始处理 {len(segment_list)} 个segment(并发限制: {MAX_CONCURRENT_EVALUATIONS})...")
+        process_tasks = [process_segment(seg) for seg in segment_list]
+        await asyncio.gather(*process_tasks)
+
+    # 打印步骤1结果
+    print(f"\n[步骤1: 分段及拆词 结果]")
+    for segment in segment_list:
+        print(f"  [{segment.type}] {segment.text} (分数: {segment.score_with_o:.2f})")
+        print(f"    拆词: {segment.words}")
+        for word in segment.words:
+            score = segment.word_scores.get(word, 0.0)
+            print(f"      - {word}: {score:.2f}")
+
+    # 保存到context(保留旧格式以兼容)
+    context.segments = [
+        {
+            "text": seg.text,
+            "type": seg.type,
+            "score": seg.score_with_o,
+            "reason": seg.reason,
+            "words": seg.words,
+            "word_scores": seg.word_scores,
+            "word_reasons": seg.word_reasons
+        }
+        for seg in segment_list
+    ]
+
+    # 保存 Round 0 到 context.rounds(新格式用于可视化)
+    context.rounds.append({
+        "round_num": 0,
+        "type": "initialization",
+        "segments": [
+            {
+                "text": seg.text,
+                "type": seg.type,
+                "domain_index": idx,
+                "score": seg.score_with_o,
+                "reason": seg.reason,
+                "words": [
+                    {
+                        "text": word,
+                        "score": seg.word_scores.get(word, 0.0),
+                        "reason": seg.word_reasons.get(word, "")
+                    }
+                    for word in seg.words
+                ]
+            }
+            for idx, seg in enumerate(segment_list)
+        ]
+    })
+
+    print(f"\n[Round 0 完成]")
+    print(f"  分段数: {len(segment_list)}")
+    total_words = sum(len(seg.words) for seg in segment_list)
+    print(f"  总词数: {total_words}")
+
+    return segment_list
+
+
+async def run_round_v2(
+    round_num: int,
+    query_input: list[Q],
+    segments: list[Segment],
+    o: str,
+    context: RunContext,
+    xiaohongshu_api: XiaohongshuSearchRecommendations,
+    xiaohongshu_search: XiaohongshuSearch,
+    sug_threshold: float = 0.7
+) -> tuple[list[Q], list[Search]]:
+    """
+    v121 Round N 执行
+
+    正确的流程顺序:
+    1. 为 query_input 请求SUG
+    2. 评估SUG
+    3. 高分SUG搜索
+    4. N域组合(从segments生成)
+    5. 评估组合
+    6. 生成 q_list_next(组合 + 高分SUG)
+
+    Args:
+        round_num: 轮次编号 (1-4)
+        query_input: 本轮的输入query列表(Round 1是words,Round 2+是上轮输出)
+        segments: 语义片段列表(用于组合)
+        o: 原始问题
+        context: 运行上下文
+        xiaohongshu_api: 建议词API
+        xiaohongshu_search: 搜索API
+        sug_threshold: SUG搜索阈值
+
+    Returns:
+        (q_list_next, search_list)
+    """
+    print(f"\n{'='*60}")
+    print(f"Round {round_num}: {round_num}域组合")
+    print(f"{'='*60}")
+
+    round_data = {
+        "round_num": round_num,
+        "n_domains": round_num,
+        "input_query_count": len(query_input)
+    }
+
+    MAX_CONCURRENT_EVALUATIONS = 5
+    semaphore = asyncio.Semaphore(MAX_CONCURRENT_EVALUATIONS)
+
+    # 步骤1: 为 query_input 请求SUG
+    print(f"\n[步骤1] 为{len(query_input)}个输入query请求SUG...")
+    all_sugs = []
+    sug_details = {}
+
+    for q in query_input:
+        suggestions = xiaohongshu_api.get_recommendations(keyword=q.text)
+        if suggestions:
+            print(f"  {q.text}: 获取到 {len(suggestions)} 个SUG")
+            for sug_text in suggestions:
+                sug = Sug(
+                    text=sug_text,
+                    from_q=QFromQ(text=q.text, score_with_o=q.score_with_o)
+                )
+                all_sugs.append(sug)
+        else:
+            print(f"  {q.text}: 未获取到SUG")
+
+    print(f"  共获取 {len(all_sugs)} 个SUG")
+
+    # 步骤2: 评估SUG
+    if len(all_sugs) > 0:
+        print(f"\n[步骤2] 评估{len(all_sugs)}个SUG...")
+
+        async def evaluate_sug(sug: Sug) -> Sug:
+            async with semaphore:
+                sug.score_with_o, sug.reason = await evaluate_with_o(
+                    sug.text, o, context.evaluation_cache
+                )
+                return sug
+
+        eval_tasks = [evaluate_sug(sug) for sug in all_sugs]
+        await asyncio.gather(*eval_tasks)
+
+        # 打印结果
+        for sug in all_sugs:
+            print(f"    {sug.text}: {sug.score_with_o:.2f}")
+            if sug.from_q:
+                if sug.from_q.text not in sug_details:
+                    sug_details[sug.from_q.text] = []
+                sug_details[sug.from_q.text].append({
+                    "text": sug.text,
+                    "score": sug.score_with_o,
+                    "reason": sug.reason,
+                    "type": "sug"
+                })
+
+    # 步骤3: 搜索高分SUG
+    print(f"\n[步骤3] 搜索高分SUG(阈值 > {sug_threshold})...")
+    high_score_sugs = [sug for sug in all_sugs if sug.score_with_o > sug_threshold]
+    print(f"  找到 {len(high_score_sugs)} 个高分SUG")
+
+    search_list = []
+    if len(high_score_sugs) > 0:
+        async def search_for_sug(sug: Sug) -> Search:
+            print(f"    搜索: {sug.text}")
+            try:
+                search_result = xiaohongshu_search.search(keyword=sug.text)
+                result_str = search_result.get("result", "{}")
+                if isinstance(result_str, str):
+                    result_data = json.loads(result_str)
+                else:
+                    result_data = result_str
+
+                notes = result_data.get("data", {}).get("data", [])
+                post_list = []
+                for note in notes[:10]:
+                    post = process_note_data(note)
+                    post_list.append(post)
+
+                print(f"      → 找到 {len(post_list)} 个帖子")
+
+                return Search(
+                    text=sug.text,
+                    score_with_o=sug.score_with_o,
+                    from_q=sug.from_q,
+                    post_list=post_list
+                )
+            except Exception as e:
+                print(f"      ✗ 搜索失败: {e}")
+                return Search(
+                    text=sug.text,
+                    score_with_o=sug.score_with_o,
+                    from_q=sug.from_q,
+                    post_list=[]
+                )
+
+        search_tasks = [search_for_sug(sug) for sug in high_score_sugs]
+        search_list = await asyncio.gather(*search_tasks)
+
+    # 步骤4: 生成N域组合
+    print(f"\n[步骤4] 生成{round_num}域组合...")
+    domain_combinations = generate_domain_combinations(segments, round_num)
+    print(f"  生成了 {len(domain_combinations)} 个组合")
+
+    if len(domain_combinations) == 0:
+        print(f"  无法生成{round_num}域组合")
+        # 即使无法组合,也返回高分SUG作为下轮输入
+        q_list_next = []
+        for sug in all_sugs:
+            if sug.from_q and sug.score_with_o >= sug.from_q.score_with_o + REQUIRED_SCORE_GAIN:
+                q = Q(
+                    text=sug.text,
+                    score_with_o=sug.score_with_o,
+                    reason=sug.reason,
+                    from_source="sug",
+                    type_label=""
+                )
+                q_list_next.append(q)
+
+        round_data.update({
+            "domain_combinations_count": 0,
+            "sug_count": len(all_sugs),
+            "high_score_sug_count": len(high_score_sugs),
+            "search_count": len(search_list),
+            "sug_details": sug_details,
+            "q_list_next_size": len(q_list_next)
+        })
+        context.rounds.append(round_data)
+        return q_list_next, search_list
+
+    # 步骤5: 评估所有组合
+    print(f"\n[步骤5] 评估{len(domain_combinations)}个组合...")
+
+    async def evaluate_combination(comb: DomainCombination) -> DomainCombination:
+        async with semaphore:
+            comb.score_with_o, comb.reason = await evaluate_with_o(
+                comb.text, o, context.evaluation_cache
+            )
+            return comb
+
+    eval_tasks = [evaluate_combination(comb) for comb in domain_combinations]
+    await asyncio.gather(*eval_tasks)
+
+    # 排序 - 已注释,保持原始顺序
+    # domain_combinations.sort(key=lambda x: x.score_with_o, reverse=True)
+
+    # 打印所有组合(保持原始顺序)
+    print(f"  评估完成,共{len(domain_combinations)}个组合:")
+    for i, comb in enumerate(domain_combinations, 1):
+        print(f"    {i}. {comb.text} {comb.type_label} (分数: {comb.score_with_o:.2f})")
+
+    # 为每个组合补充来源词分数信息,并判断是否超过所有来源词得分
+    for comb in domain_combinations:
+        word_details = []
+        flat_scores: list[float] = []
+        for domain_index, words in zip(comb.domains, comb.source_words):
+            segment = segments[domain_index] if 0 <= domain_index < len(segments) else None
+            segment_type = segment.type if segment else ""
+            segment_text = segment.text if segment else ""
+            items = []
+            for word in words:
+                score = 0.0
+                if segment and word in segment.word_scores:
+                    score = segment.word_scores[word]
+                items.append({
+                    "text": word,
+                    "score": score
+                })
+                flat_scores.append(score)
+            word_details.append({
+                "domain_index": domain_index,
+                "segment_type": segment_type,
+                "segment_text": segment_text,
+                "words": items
+            })
+        comb.source_word_details = word_details
+        comb.source_scores = flat_scores
+        comb.max_source_score = max(flat_scores) if flat_scores else None
+        comb.is_above_source_scores = bool(flat_scores) and all(
+            comb.score_with_o > score for score in flat_scores
+        )
+
+    # 步骤6: 构建 q_list_next(组合 + 高分SUG)
+    print(f"\n[步骤6] 生成下轮输入...")
+    q_list_next: list[Q] = []
+
+    # 6.1 添加高增益SUG(满足增益条件),并按分数排序
+    sug_candidates: list[tuple[Q, Sug]] = []
+    for sug in all_sugs:
+        if sug.from_q and sug.score_with_o >= sug.from_q.score_with_o + REQUIRED_SCORE_GAIN:
+            q = Q(
+                text=sug.text,
+                score_with_o=sug.score_with_o,
+                reason=sug.reason,
+                from_source="sug",
+                type_label=""
+            )
+            sug_candidates.append((q, sug))
+
+    sug_candidates.sort(key=lambda item: item[0].score_with_o, reverse=True)
+    q_list_next.extend([item[0] for item in sug_candidates])
+    high_gain_sugs = [item[1] for item in sug_candidates]
+    print(f"  添加 {len(high_gain_sugs)} 个高增益SUG(增益 ≥ {REQUIRED_SCORE_GAIN:.2f})")
+
+    # 6.2 添加高分组合(需超过所有来源词得分),并按分数排序
+    combination_candidates: list[tuple[Q, DomainCombination]] = []
+    for comb in domain_combinations:
+        if comb.is_above_source_scores and comb.score_with_o > 0:
+            domains_str = ','.join([f'D{d}' for d in comb.domains]) if comb.domains else ''
+            q = Q(
+                text=comb.text,
+                score_with_o=comb.score_with_o,
+                reason=comb.reason,
+                from_source="domain_comb",
+                type_label=comb.type_label,
+                domain_type=domains_str  # 添加域信息
+            )
+            combination_candidates.append((q, comb))
+
+    combination_candidates.sort(key=lambda item: item[0].score_with_o, reverse=True)
+    q_list_next.extend([item[0] for item in combination_candidates])
+    high_score_combinations = [item[1] for item in combination_candidates]
+    print(f"  添加 {len(high_score_combinations)} 个高分组合(组合得分 > 所有来源词)")
+
+    # 保存round数据(包含完整帖子信息)
+    search_results_data = []
+    for search in search_list:
+        search_results_data.append({
+            "text": search.text,
+            "score_with_o": search.score_with_o,
+            "post_list": [
+                {
+                    "note_id": post.note_id,
+                    "note_url": post.note_url,
+                    "title": post.title,
+                    "body_text": post.body_text,
+                    "images": post.images,
+                    "interact_info": post.interact_info
+                }
+                for post in search.post_list
+            ]
+        })
+
+    round_data.update({
+        "input_queries": [{"text": q.text, "score": q.score_with_o, "from_source": q.from_source, "type": "input", "domain_index": q.domain_index, "domain_type": q.domain_type} for q in query_input],
+        "domain_combinations_count": len(domain_combinations),
+        "domain_combinations": [
+            {
+                "text": comb.text,
+                "type_label": comb.type_label,
+                "score": comb.score_with_o,
+                "reason": comb.reason,
+                "domains": comb.domains,
+                "source_words": comb.source_words,
+                "from_segments": comb.from_segments,
+                "source_word_details": comb.source_word_details,
+                "source_scores": comb.source_scores,
+                "is_above_source_scores": comb.is_above_source_scores,
+                "max_source_score": comb.max_source_score
+            }
+            for comb in domain_combinations
+        ],
+        "high_score_combinations": [
+            {
+                "text": item[0].text,
+                "score": item[0].score_with_o,
+                "type_label": item[0].type_label,
+                "type": "combination",
+                "is_above_source_scores": item[1].is_above_source_scores
+            }
+            for item in combination_candidates
+        ],
+        "sug_count": len(all_sugs),
+        "sug_details": sug_details,
+        "high_score_sug_count": len(high_score_sugs),
+        "high_gain_sugs": [{"text": q.text, "score": q.score_with_o, "type": "sug"} for q in q_list_next if q.from_source == "sug"],
+        "search_count": len(search_list),
+        "search_results": search_results_data,
+        "q_list_next_size": len(q_list_next),
+        "q_list_next_sections": {
+            "sugs": [
+                {
+                    "text": item[0].text,
+                    "score": item[0].score_with_o,
+                    "from_source": "sug"
+                }
+                for item in sug_candidates
+            ],
+            "domain_combinations": [
+                {
+                    "text": item[0].text,
+                    "score": item[0].score_with_o,
+                    "from_source": "domain_comb",
+                    "is_above_source_scores": item[1].is_above_source_scores
+                }
+                for item in combination_candidates
+            ]
+        }
+    })
+    context.rounds.append(round_data)
+
+    print(f"\nRound {round_num} 总结:")
+    print(f"  输入Query数: {len(query_input)}")
+    print(f"  域组合数: {len(domain_combinations)}")
+    print(f"  高分组合: {len(high_score_combinations)}")
+    print(f"  SUG数: {len(all_sugs)}")
+    print(f"  高分SUG数: {len(high_score_sugs)}")
+    print(f"  高增益SUG: {len(high_gain_sugs)}")
+    print(f"  搜索数: {len(search_list)}")
+    print(f"  下轮Query数: {len(q_list_next)}")
+
+    return q_list_next, search_list
+
+
+async def iterative_loop_v2(
+    context: RunContext,
+    max_rounds: int = 4,
+    sug_threshold: float = 0.7
+):
+    """v121 主迭代循环"""
+
+    print(f"\n{'='*60}")
+    print(f"开始v121迭代循环(语义分段跨域组词版)")
+    print(f"最大轮数: {max_rounds}")
+    print(f"sug阈值: {sug_threshold}")
+    print(f"{'='*60}")
+
+    # Round 0: 初始化(语义分段 + 拆词)
+    segments = await initialize_v2(context.o, context)
+
+    # API实例
+    xiaohongshu_api = XiaohongshuSearchRecommendations()
+    xiaohongshu_search = XiaohongshuSearch()
+
+    # 收集所有搜索结果
+    all_search_list = []
+
+    # 准备 Round 1 的输入:从 segments 提取所有 words
+    query_input = extract_words_from_segments(segments)
+    print(f"\n提取了 {len(query_input)} 个词作为 Round 1 的输入")
+
+    # Round 1-N: 迭代循环
+    num_segments = len(segments)
+    actual_max_rounds = min(max_rounds, num_segments)
+    round_num = 1
+
+    while query_input and round_num <= actual_max_rounds:
+        query_input, search_list = await run_round_v2(
+            round_num=round_num,
+            query_input=query_input,  # 传递上一轮的输出
+            segments=segments,
+            o=context.o,
+            context=context,
+            xiaohongshu_api=xiaohongshu_api,
+            xiaohongshu_search=xiaohongshu_search,
+            sug_threshold=sug_threshold
+        )
+
+        all_search_list.extend(search_list)
+
+        # 如果没有新的query,提前结束
+        if not query_input:
+            print(f"\n第{round_num}轮后无新query生成,提前结束迭代")
+            break
+
+        round_num += 1
+
+    print(f"\n{'='*60}")
+    print(f"迭代完成")
+    print(f"  实际轮数: {round_num}")
+    print(f"  总搜索次数: {len(all_search_list)}")
+    print(f"  总帖子数: {sum(len(s.post_list) for s in all_search_list)}")
+    print(f"{'='*60}")
+
+    return all_search_list
+
+
+# ============================================================================
+# 主函数
+# ============================================================================
+
+async def main(input_dir: str, max_rounds: int = 2, sug_threshold: float = 0.7, visualize: bool = False):
+    """主函数"""
+    current_time, log_url = set_trace()
+
+    # 读取输入
+    input_context_file = os.path.join(input_dir, 'context.md')
+    input_q_file = os.path.join(input_dir, 'q.md')
+
+    c = read_file_as_string(input_context_file)  # 原始需求
+    o = read_file_as_string(input_q_file)  # 原始问题
+
+    # 版本信息
+    version = os.path.basename(__file__)
+    version_name = os.path.splitext(version)[0]
+
+    # 日志目录
+    log_dir = os.path.join(input_dir, "output", version_name, current_time)
+
+    # 创建运行上下文
+    run_context = RunContext(
+        version=version,
+        input_files={
+            "input_dir": input_dir,
+            "context_file": input_context_file,
+            "q_file": input_q_file,
+        },
+        c=c,
+        o=o,
+        log_dir=log_dir,
+        log_url=log_url,
+    )
+
+    # 创建日志目录
+    os.makedirs(run_context.log_dir, exist_ok=True)
+
+    # 配置日志文件
+    log_file_path = os.path.join(run_context.log_dir, "run.log")
+    log_file = open(log_file_path, 'w', encoding='utf-8')
+
+    # 重定向stdout到TeeLogger(同时输出到控制台和文件)
+    original_stdout = sys.stdout
+    sys.stdout = TeeLogger(original_stdout, log_file)
+
+    try:
+        print(f"📝 日志文件: {log_file_path}")
+        print(f"{'='*60}\n")
+
+        # 执行迭代 (v121: 使用新架构)
+        all_search_list = await iterative_loop_v2(
+            run_context,
+            max_rounds=max_rounds,
+            sug_threshold=sug_threshold
+        )
+
+        # 格式化输出
+        output = f"原始需求:{run_context.c}\n"
+        output += f"原始问题:{run_context.o}\n"
+        output += f"总搜索次数:{len(all_search_list)}\n"
+        output += f"总帖子数:{sum(len(s.post_list) for s in all_search_list)}\n"
+        output += "\n" + "="*60 + "\n"
+
+        if all_search_list:
+            output += "【搜索结果】\n\n"
+            for idx, search in enumerate(all_search_list, 1):
+                output += f"{idx}. 搜索词: {search.text} (分数: {search.score_with_o:.2f})\n"
+                output += f"   帖子数: {len(search.post_list)}\n"
+                if search.post_list:
+                    for post_idx, post in enumerate(search.post_list[:3], 1):  # 只显示前3个
+                        output += f"   {post_idx}) {post.title}\n"
+                        output += f"      URL: {post.note_url}\n"
+                output += "\n"
+        else:
+            output += "未找到搜索结果\n"
+
+        run_context.final_output = output
+
+        print(f"\n{'='*60}")
+        print("最终结果")
+        print(f"{'='*60}")
+        print(output)
+
+        # 保存上下文文件
+        context_file_path = os.path.join(run_context.log_dir, "run_context.json")
+        context_dict = run_context.model_dump()
+        with open(context_file_path, "w", encoding="utf-8") as f:
+            json.dump(context_dict, f, ensure_ascii=False, indent=2)
+        print(f"\nRunContext saved to: {context_file_path}")
+
+        # 保存详细的搜索结果
+        search_results_path = os.path.join(run_context.log_dir, "search_results.json")
+        search_results_data = [s.model_dump() for s in all_search_list]
+        with open(search_results_path, "w", encoding="utf-8") as f:
+            json.dump(search_results_data, f, ensure_ascii=False, indent=2)
+        print(f"Search results saved to: {search_results_path}")
+
+        # 可视化
+        if visualize:
+            import subprocess
+            output_html = os.path.join(run_context.log_dir, "visualization.html")
+            print(f"\n🎨 生成可视化HTML...")
+
+            # 获取绝对路径
+            abs_context_file = os.path.abspath(context_file_path)
+            abs_output_html = os.path.abspath(output_html)
+
+            # 运行可视化脚本
+            result = subprocess.run([
+                "node",
+                "visualization/sug_v6_1_2_121/index.js",
+                abs_context_file,
+                abs_output_html
+            ])
+
+            if result.returncode == 0:
+                print(f"✅ 可视化已生成: {output_html}")
+            else:
+                print(f"❌ 可视化生成失败")
+
+    finally:
+        # 恢复stdout
+        sys.stdout = original_stdout
+        log_file.close()
+        print(f"\n📝 运行日志已保存: {log_file_path}")
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description="搜索query优化工具 - v6.1.2.121 语义分段跨域组词版")
+    parser.add_argument(
+        "--input-dir",
+        type=str,
+        default="input/旅游-逸趣玩旅行/如何获取能体现川西秋季特色的高质量风光摄影素材?",
+        help="输入目录路径,默认: input/旅游-逸趣玩旅行/如何获取能体现川西秋季特色的高质量风光摄影素材?"
+    )
+    parser.add_argument(
+        "--max-rounds",
+        type=int,
+        default=4,
+        help="最大轮数,默认: 4"
+    )
+    parser.add_argument(
+        "--sug-threshold",
+        type=float,
+        default=0.7,
+        help="suggestion阈值,默认: 0.7"
+    )
+    parser.add_argument(
+        "--visualize",
+        action="store_true",
+        default=True,
+        help="运行完成后自动生成可视化HTML"
+    )
+    args = parser.parse_args()
+
+    asyncio.run(main(args.input_dir, max_rounds=args.max_rounds, sug_threshold=args.sug_threshold, visualize=args.visualize))

+ 3894 - 0
sug_v6_1_2_126.py

@@ -0,0 +1,3894 @@
+import asyncio
+import json
+import os
+import sys
+import argparse
+from datetime import datetime
+from typing import Literal, Optional
+
+from agents import Agent, Runner, ModelSettings
+from lib.my_trace import set_trace
+from pydantic import BaseModel, Field
+
+from lib.utils import read_file_as_string
+from lib.client import get_model
+MODEL_NAME = "google/gemini-2.5-flash"
+# 得分提升阈值:sug或组合词必须比来源query提升至少此幅度才能进入下一轮
+REQUIRED_SCORE_GAIN = 0.02
+from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
+from script.search.xiaohongshu_search import XiaohongshuSearch
+
+
+# ============================================================================
+# 日志工具类
+# ============================================================================
+
+class TeeLogger:
+    """同时输出到控制台和日志文件的工具类"""
+    def __init__(self, stdout, log_file):
+        self.stdout = stdout
+        self.log_file = log_file
+
+    def write(self, message):
+        self.stdout.write(message)
+        self.log_file.write(message)
+        self.log_file.flush()  # 实时写入,避免丢失日志
+
+    def flush(self):
+        self.stdout.flush()
+        self.log_file.flush()
+
+
+# ============================================================================
+# 数据模型
+# ============================================================================
+
+class Seg(BaseModel):
+    """分词(旧版)- v120使用"""
+    text: str
+    score_with_o: float = 0.0  # 与原始问题的评分
+    reason: str = ""  # 评分理由
+    from_o: str = ""  # 原始问题
+
+
+# ============================================================================
+# 新架构数据模型 (v121)
+# ============================================================================
+
+class Segment(BaseModel):
+    """语义片段(Round 0语义分段结果 - 谓语+作用域方式)"""
+    text: str  # 片段文本
+    type: str  # 类型: interrogative/scope(scopes已包含谓语)
+    scope_type: Optional[str] = None  # 如果type是scope,记录作用域类型(predicate_feature/predicate_object/predicate_full/feature/object/full/location/time/composite等)
+    is_complete: Optional[bool] = None  # 如果type是scope,记录是否语义完整
+    score_with_o: float = 0.0  # 与原始问题的评分
+    reason: str = ""  # 评分理由
+    from_o: str = ""  # 原始问题
+    words: list[str] = Field(default_factory=list)  # 该片段拆分出的词列表(Round 0拆词结果)
+    word_scores: dict[str, float] = Field(default_factory=dict)  # 词的评分 {word: score}
+    word_reasons: dict[str, float] = Field(default_factory=dict)  # 词的评分理由 {word: reason}
+
+
+class DomainCombination(BaseModel):
+    """域组合(Round N的N域组合结果)"""
+    text: str  # 组合后的文本
+    domains: list[int] = Field(default_factory=list)  # 参与组合的域索引列表(对应segments的索引)
+    type_label: str = ""  # 类型标签,如 [疑问标记+核心动作+中心名词]
+    source_words: list[list[str]] = Field(default_factory=list)  # 来源词列表,每个元素是一个域的词列表,如 [["猫咪"], ["梗图"]]
+    score_with_o: float = 0.0  # 与原始问题的评分
+    reason: str = ""  # 评分理由
+    from_segments: list[str] = Field(default_factory=list)  # 来源segment的文本列表
+    source_word_details: list[dict] = Field(default_factory=list)  # 词及其得分信息 [{"domain_index":0,"segment_type":"","words":[{"text":"","score":0.0}]}]
+    source_scores: list[float] = Field(default_factory=list)  # 来源词的分数列表(扁平化)
+    max_source_score: float | None = None  # 来源词的最高分
+    is_above_source_scores: bool = False  # 组合得分是否超过所有来源词
+
+
+# ============================================================================
+# 旧架构数据模型(保留但不使用)
+# ============================================================================
+
+# class Word(BaseModel):
+#     """词(旧版)- v120使用,v121不再使用"""
+#     text: str
+#     score_with_o: float = 0.0  # 与原始问题的评分
+#     from_o: str = ""  # 原始问题
+
+
+class Word(BaseModel):
+    """词"""
+    text: str
+    score_with_o: float = 0.0  # 与原始问题的评分
+    from_o: str = ""  # 原始问题
+
+
+class QFromQ(BaseModel):
+    """Q来源信息(用于Sug中记录)"""
+    text: str
+    score_with_o: float = 0.0
+
+
+class Q(BaseModel):
+    """查询"""
+    text: str
+    score_with_o: float = 0.0  # 与原始问题的评分
+    reason: str = ""  # 评分理由
+    from_source: str = ""  # v120: seg/sug/add; v121新增: segment/domain_comb/sug
+    type_label: str = ""  # v121新增:域类型标签(仅用于domain_comb来源)
+    domain_index: int = -1  # v121新增:域索引(word来源时有效,-1表示无域)
+    domain_type: str = ""  # v121新增:域类型(word来源时表示所属segment的type,如"中心名词")
+
+
+class Sug(BaseModel):
+    """建议词"""
+    text: str
+    score_with_o: float = 0.0  # 与原始问题的评分
+    reason: str = ""  # 评分理由
+    from_q: QFromQ | None = None  # 来自的q
+
+
+class Seed(BaseModel):
+    """种子(旧版)- v120使用,v121不再使用"""
+    text: str
+    added_words: list[str] = Field(default_factory=list)  # 已经增加的words
+    from_type: str = ""  # seg/sug/add
+    score_with_o: float = 0.0  # 与原始问题的评分
+
+
+class Post(BaseModel):
+    """帖子"""
+    title: str = ""
+    body_text: str = ""
+    type: str = "normal"  # video/normal
+    images: list[str] = Field(default_factory=list)  # 图片url列表,第一张为封面
+    video: str = ""  # 视频url
+    interact_info: dict = Field(default_factory=dict)  # 互动信息
+    note_id: str = ""
+    note_url: str = ""
+
+
+class Search(Sug):
+    """搜索结果(继承Sug)"""
+    post_list: list[Post] = Field(default_factory=list)  # 搜索得到的帖子列表
+
+
+class RunContext(BaseModel):
+    """运行上下文"""
+    version: str
+    input_files: dict[str, str]
+    c: str  # 原始需求
+    o: str  # 原始问题
+    log_url: str
+    log_dir: str
+
+    # v121新增:语义分段结果
+    segments: list[dict] = Field(default_factory=list)  # Round 0的语义分段结果
+
+    # 每轮的数据
+    rounds: list[dict] = Field(default_factory=list)  # 每轮的详细数据
+
+    # 最终结果
+    final_output: str | None = None
+
+    # 评估缓存:避免重复评估相同文本
+    evaluation_cache: dict[str, tuple[float, str]] = Field(default_factory=dict)
+    # key: 文本, value: (score, reason)
+
+
+# ============================================================================
+# Agent 定义
+# ============================================================================
+
+# ============================================================================
+# v121 新增 Agent
+# ============================================================================
+
+# Agent: 语义分段专家 (Prompt1 - 改为谓语+作用域方式)
+class ScopeFragment(BaseModel):
+    """作用域片段(不要求语义完整)"""
+    text: str = Field(..., description="作用域文本")
+    scope_type: str = Field(..., description="类型:feature/object/full/location/time/composite等")
+    is_complete: bool = Field(..., description="是否语义完整(仅标注,不强制要求)")
+    reasoning: str = Field(..., description="为什么识别为此作用域片段")
+
+
+class QuerySegmentation(BaseModel):
+    """Query分段结果(谓语+作用域方式)"""
+    interrogative: Optional[str] = Field(None, description="疑问词(如何、怎么等),可选")
+    predicate: Optional[str] = Field(None, description="谓语动词,可选(纯名词短语时为空)")
+    scopes: list[ScopeFragment] = Field(default_factory=list, description="作用域片段列表,可以有多个层次")
+    overall_reasoning: str = Field(..., description="整体分段思路")
+
+
+semantic_segmentation_instructions = """
+你是语义分段专家。给定一个搜索query,识别谓语和作用域。
+
+## 核心概念
+- **谓语**:核心动词,表示"做什么动作"(如:制作、获取、学习)
+- **作用域**:谓语的作用对象及修饰成分,可以有多个层次
+- **疑问引导**:疑问词(如何、怎么、什么等)
+
+## 分段原则
+1. **识别疑问词**(可选):如"如何"、"怎么"、"为什么"
+2. **识别谓语**:核心动词
+3. **识别作用域的多个层次**:
+   - ❗ 不要求语义完整
+   - ✅ 只要有独立的检索价值即可
+   - ✅ 可以是修饰短语、名词短语、或完整宾语
+   - ✅ 可以识别出多个不同粒度的作用域片段
+
+## 作用域类型
+
+### 有谓语时(scopes包含谓语):
+- **predicate_feature**: 谓语+特征修饰(如"制作反映人类双标行为")
+- **predicate_object**: 谓语+核心对象(如"制作猫咪表情包梗图")
+- **predicate_full**: 谓语+完整作用域(如"制作反映人类双标行为的猫咪表情包梗图")
+
+### 无谓语时(纯名词短语):
+- **location**: 地点限定(如"川西")
+- **time**: 时间限定(如"秋季")
+- **feature**: 主题特征(如"风光")
+- **object**: 核心对象(如"摄影素材")
+- **composite**: 复合片段(如"川西风光"、"风光摄影素材")
+- **full**: 完整query(如"川西秋季风光摄影素材")
+
+## 示例1
+Input: "如何制作反映人类双标行为的猫咪表情包梗图"
+
+Output:
+{
+  "interrogative": "如何",
+  "predicate": "制作",
+  "scopes": [
+    {
+      "text": "制作反映人类双标行为",
+      "scope_type": "predicate_feature",
+      "is_complete": false,
+      "reasoning": "谓语+特征修饰,虽然缺少名词但有独立检索价值,可用于查找制作所有反映双标行为的内容"
+    },
+    {
+      "text": "制作猫咪表情包梗图",
+      "scope_type": "predicate_object",
+      "is_complete": true,
+      "reasoning": "谓语+核心对象,明确的检索目标"
+    },
+    {
+      "text": "制作反映人类双标行为的猫咪表情包梗图",
+      "scope_type": "predicate_full",
+      "is_complete": true,
+      "reasoning": "谓语+完整作用域,包含所有修饰和核心对象,最精确的匹配"
+    }
+  ],
+  "overall_reasoning": "Query包含疑问引导词和谓语,scopes直接包含谓语与其作用域的组合,分为特征层、对象层和完整层三个检索粒度"
+}
+
+## 示例2
+Input: "川西秋季风光摄影素材"
+
+Output:
+{
+  "interrogative": null,
+  "predicate": null,
+  "scopes": [
+    {
+      "text": "川西",
+      "scope_type": "location",
+      "is_complete": false,
+      "reasoning": "地点限定,独立检索价值"
+    },
+    {
+      "text": "秋季",
+      "scope_type": "time",
+      "is_complete": false,
+      "reasoning": "时间限定,独立检索价值"
+    },
+    {
+      "text": "风光",
+      "scope_type": "feature",
+      "is_complete": false,
+      "reasoning": "主题特征"
+    },
+    {
+      "text": "摄影素材",
+      "scope_type": "object",
+      "is_complete": true,
+      "reasoning": "核心对象"
+    },
+    {
+      "text": "川西风光",
+      "scope_type": "composite",
+      "is_complete": false,
+      "reasoning": "地点+主题的组合,常见检索组合"
+    },
+    {
+      "text": "风光摄影素材",
+      "scope_type": "composite",
+      "is_complete": true,
+      "reasoning": "主题+对象的组合"
+    },
+    {
+      "text": "川西秋季风光摄影素材",
+      "scope_type": "full",
+      "is_complete": true,
+      "reasoning": "完整query,最精确匹配"
+    }
+  ],
+  "overall_reasoning": "纯名词短语,无谓语,包含地点、时间、主题、对象等多个维度,提供多个检索粒度"
+}
+
+## 示例3
+Input: "获取素材"
+
+Output:
+{
+  "interrogative": null,
+  "predicate": "获取",
+  "scopes": [
+    {
+      "text": "获取素材",
+      "scope_type": "predicate_object",
+      "is_complete": true,
+      "reasoning": "谓语+核心对象,简单的动宾结构"
+    }
+  ],
+  "overall_reasoning": "简单的动宾结构,scopes直接包含谓语与对象的组合"
+}
+
+## 输出要求
+- interrogative: 疑问词(可选)
+- predicate: 谓语动词(可选,纯名词短语时为null)
+- scopes: 作用域片段列表,包含多个检索粒度
+  - text: 作用域文本
+  - scope_type: 类型标注
+  - is_complete: 是否语义完整(标注用,不影响检索)
+  - reasoning: 识别理由
+- overall_reasoning: 整体分段思路
+
+## 注意事项
+1. 作用域可以重叠(如"川西"和"川西风光"都是有效的)
+2. 不要求每个作用域都语义完整
+3. 优先识别有实际检索价值的片段
+4. 避免过度拆分(如不要拆成单字)
+5. JSON中使用《》或「」代替引号
+""".strip()
+
+semantic_segmenter = Agent[None](
+    name="语义分段专家",
+    instructions=semantic_segmentation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=QuerySegmentation,
+)
+
+
+# ============================================================================
+# v120 保留 Agent
+# ============================================================================
+
+# Agent 1: 分词专家(v121用于Round 0拆词)
+class WordSegmentation(BaseModel):
+    """分词结果"""
+    words: list[str] = Field(..., description="分词结果列表")
+    reasoning: str = Field(..., description="分词理由")
+
+word_segmentation_instructions = """
+你是分词专家。给定一个query,将其拆分成有意义的最小单元。
+
+## 分词原则
+1. 保留有搜索意义的词汇
+2. 拆分成独立的概念
+3. 保留专业术语的完整性
+4. 去除虚词(的、吗、呢等),但保留疑问词(如何、为什么、怎样等)
+
+## 输出要求
+返回分词列表和分词理由。
+""".strip()
+
+word_segmenter = Agent[None](
+    name="分词专家",
+    instructions=word_segmentation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=WordSegmentation,
+)
+
+
+# Agent 2: 动机维度评估专家 + 品类维度评估专家(两阶段评估)
+
+# 动机评估的嵌套模型
+class CoreMotivationExtraction(BaseModel):
+    """核心动机提取"""
+    简要说明核心动机: str = Field(..., description="核心动机说明")
+
+class MotivationEvaluation(BaseModel):
+    """动机维度评估"""
+    原始问题核心动机提取: CoreMotivationExtraction = Field(..., description="原始问题核心动机提取")
+    动机维度得分: float = Field(..., description="动机维度得分 -1~1")
+    简要说明动机维度相关度理由: str = Field(..., description="动机维度相关度理由")
+    得分为零的原因: Optional[Literal["原始问题无动机", "sug词条无动机", "动机不匹配", "不适用"]] = Field(None, description="当得分为0时的原因分类(可选,仅SUG评估使用)")
+
+class CategoryEvaluation(BaseModel):
+    """品类维度评估"""
+    品类维度得分: float = Field(..., description="品类维度得分 -1~1")
+    简要说明品类维度相关度理由: str = Field(..., description="品类维度相关度理由")
+
+class ExtensionWordEvaluation(BaseModel):
+    """延伸词评估"""
+    延伸词得分: float = Field(..., ge=-1, le=1, description="延伸词得分 -1~1")
+    简要说明延伸词维度相关度理由: str = Field(..., description="延伸词维度相关度理由")
+
+# 动机评估 prompt(统一版本)
+motivation_evaluation_instructions = """
+# 角色
+你是**专业的动机意图评估专家**。
+任务:判断<平台sug词条>与<原始问题>的**动机意图匹配度**,给出**-1到1之间**的数值评分。
+
+---
+# 输入信息
+你将接收到以下输入:
+- **<原始问题>**:用户的初始查询问题,代表用户的真实需求意图。
+- **<平台sug词条>**:待评估的词条,可能是单个或多个作用域的组合
+---
+
+
+# 核心约束
+
+## 维度独立性声明
+【严格约束】本评估**仅评估动机意图维度**:
+- **只评估** 用户"想要做什么",即原始问题的行为意图和目的
+- 核心是 **动词**:获取、学习、拍摄、制作、寻找等
+- 包括:核心动作 + 使用场景 + 最终目的
+- **评估重点**:动作本身及其语义方向
+ **禁止使用"主题相关"作为评分依据**:评分理由中不得出现"主题"、"内容"、"话题"等词
+
+---
+
+# 作用域与动作意图
+
+## 什么是作用域?
+**作用域 = 动机层 + 对象层 + 场景层**
+
+## 动作意图的识别
+
+### 方法1: 显性动词直接提取
+
+当原始问题明确包含动词时,直接提取
+示例:
+"如何获取素材" → 核心动机 = "获取"
+"寻找拍摄技巧" → 核心动机 = "寻找"(或"学习")
+"制作视频教程" → 核心动机 = "制作"
+
+### 方法2: 隐性动词语义推理
+当原始问题没有显性动词时,需要结合上下文推理
+
+如果原始问题是纯名词短语,无任何动作线索:
+→ 核心动机 = 无法识别
+→ 在此情况下,动机维度得分应为 0。
+示例:
+"摄影" → 无法识别动机,动机维度得分 = 0
+"川西风光" → 无法识别动机,动机维度得分 = 0
+
+---
+
+# 部分作用域的处理
+
+## 情况1:sug词条是原始问题的部分作用域
+
+当sug词条只包含原始问题的部分作用域时,需要判断:
+1. sug词条是否包含动作意图
+2. 如果包含,动作是否匹配
+
+**示例**:
+```
+原始问题:"川西旅行行程规划"
+- 完整作用域:规划(动作)+ 旅行行程(对象)+ 川西(场景)
+
+Sug词条:"川西旅行"
+- 包含作用域:旅行(部分对象)+ 川西(场景)
+- 缺失作用域:规划(动作)
+- 动作意图评分:0(无动作意图)
+```
+
+**评分原则**:
+- 如果sug词条缺失动机层(动作) → 动作意图得分 = 0
+- 如果sug词条包含动机层 → 按动作匹配度评分
+
+---
+
+# 评分标准
+
+## 【正向匹配】
+
+### +0.9~1.0:核心动作完全一致
+**示例**:
+- "规划旅行行程" vs "安排旅行路线" → 0.98
+  - 规划≈安排,语义完全一致
+- "获取素材" vs "下载素材" → 0.97
+  - 获取≈下载,语义完全一致
+
+- 特殊规则: 如果sug词的核心动作是原始问题动作的**具体化子集**,也判定为完全一致
+例: 原始问题"扣除猫咪主体的方法" vs sug词"扣除猫咪眼睛的方法"(子集但目的一致
+**注意**:此处不考虑对象和场景是否一致,只看动作本身
+
+###+0.75~0.95: 核心动作语义相近或为同义表达
+  - 例: 原始问题"如何获取素材" vs sug词"如何下载素材"
+  - 同义词对: 获取≈下载≈寻找, 技巧≈方法≈教程≈攻略
+
+### +0.50~0.75:动作意图相关
+**判定标准**:
+- 动作是实现原始意图的相关路径
+- 或动作是原始意图的前置/后置步骤
+
+**示例**:
+- "获取素材" vs "管理素材" → 0.65
+  - 管理是获取后的相关步骤
+- "规划行程" vs "预订酒店" → 0.60
+  - 预订是规划的具体实施步骤
+
+### +0.25~0.50:动作意图弱相关
+**判定标准**:
+- 动作在同一大类但方向不同
+- 或动作有间接关联
+
+**示例**:
+- "学习摄影技巧" vs "欣赏摄影作品" → 0.35
+  - 都与摄影有关,但学习≠欣赏
+- "规划旅行" vs "回忆旅行" → 0.30
+  - 都与旅行有关,但方向不同
+
+---
+
+## 【中性/无关】
+
+### 0:无动作意图或动作完全无关
+**适用场景**:
+1. 原始问题或sug词条无法识别动作
+2. 两者动作意图完全无关
+
+**示例**:
+- "如何获取素材" vs "摄影器材" → 0
+  - sug词条无动作意图
+- "川西风光" vs "风光摄影作品" → 0
+  - 原始问题无动作意图
+
+**理由模板**:
+- "sug词条无明确动作意图,无法评估动作匹配度"
+- "原始问题无明确动作意图,动作维度得分为0"
+
+---
+
+## 【负向偏离】
+
+### -0.2~-0.05:动作方向轻度偏离
+**示例**:
+- "学习摄影技巧" vs "销售摄影课程" → -0.10
+  - 学习 vs 销售,方向有偏差
+
+### -0.5~-0.25:动作意图明显冲突
+**示例**:
+- "获取免费素材" vs "购买素材" → -0.35
+  - 获取免费 vs 购买,明显冲突
+
+### -1.0~-0.55:动作意图完全相反
+**示例**:
+- "下载素材" vs "上传素材" → -0.70
+  - 下载 vs 上传,方向完全相反
+
+---
+
+## 得分为零的原因(语义判断)
+
+当动机维度得分为 0 时,需要在 `得分为零的原因` 字段中选择以下之一:
+- **"原始问题无动机"**:原始问题是纯名词短语,无法识别任何动作意图
+- **"sug词条无动机"**:sug词条中不包含任何动作意图
+- **"动机不匹配"**:双方都有动作,但完全无关联
+- **"不适用"**:得分不为零时使用此默认值
+
+---
+
+# 输出格式
+输出结果必须为一个 **JSON 格式**,包含以下内容:
+```json
+{
+  "原始问题核心动机提取": {
+    "简要说明核心动机": ""
+  },
+  "动机维度得分": "-1到1之间的小数",
+  "简要说明动机维度相关度理由": "评估该sug词条与原始问题动机匹配程度的理由,包含作用域覆盖情况",
+  "得分为零的原因": "原始问题无动机/sug词条无动机/动机不匹配/不适用"
+}
+```
+
+**输出约束(非常重要)**:
+1. **字符串长度限制**:\"简要说明动机维度相关度理由\"字段必须控制在**150字以内**
+2. **JSON格式规范**:必须生成完整的JSON格式,确保字符串用双引号包裹且正确闭合
+3. **引号使用**:字符串中如需表达引用,请使用《》或「」代替单引号或双引号
+
+---
+
+# 核心原则总结
+1. **只评估动作**:完全聚焦于动作意图,不管对象和场景
+2. **作用域识别**:识别作用域但只评估动机层
+3. **严格标准一致性**:对所有用例使用相同的评估标准,避免评分飘移
+4. **理由纯粹**:评分理由只能谈动作,不能谈对象、场景、主题
+""".strip()
+
+# 品类评估 prompt
+category_evaluation_instructions = """
+# 角色
+你是**专业的内容主体评估专家**。
+任务:判断<平台sug词条>与<原始问题>的**内容主体匹配度**,给出**-1到1之间**的数值评分。
+
+---
+
+# 输入信息
+- **<原始问题>**:用户的完整需求描述
+- **<平台sug词条>**:待评估的词条,可能是单个或多个作用域的组合
+---
+
+
+# 核心约束
+
+## 维度独立性声明
+【严格约束】本评估**仅评估内容主体维度**:
+- **只评估**:名词主体 + 限定词(地域、时间、场景、质量等)
+- **完全忽略**:动作、意图、目的
+- **评估重点**:内容本身的主题和属性
+
+---
+
+# 作用域与内容主体
+
+## 什么是作用域?
+**作用域 = 动机层 + 对象层 + 场景层**
+
+在Prompt2中:
+- **动机层(动作)完全忽略**
+- **只评估对象层 + 场景层(限定词)**
+
+## 内容主体的构成
+
+**内容主体 = 核心名词 + 限定词**
+
+
+---
+
+# 作用域覆盖度评估
+
+## 核心原则:越完整越高分
+
+**完整性公式**:
+```
+作用域覆盖度 = sug词条包含的作用域元素 / 原始问题的作用域元素总数
+```
+
+**评分影响**:
+- 覆盖度100% → 基础高分(0.9+)
+- 覆盖度50-99% → 中高分(0.6-0.9)
+- 覆盖度<50% → 中低分(0.3-0.6)
+- 覆盖度=0 → 低分或0分
+
+---
+
+## 部分作用域的处理
+
+### 情况1:sug词条包含原始问题的所有对象层和场景层元素
+**评分**:0.95-1.0
+
+**示例**:
+```
+原始问题:"川西秋季风光摄影素材"
+- 对象层:摄影素材
+- 场景层:川西 + 秋季 + 风光
+
+Sug词条:"川西秋季风光摄影作品"
+- 对象层:摄影作品(≈素材)
+- 场景层:川西 + 秋季 + 风光
+- 覆盖度:100%
+- 评分:0.98
+```
+
+### 情况2:sug词条包含部分场景层元素
+**评分**:根据覆盖比例
+
+**示例**:
+```
+原始问题:"川西秋季风光摄影素材"
+- 对象层:摄影素材
+- 场景层:川西 + 秋季 + 风光(3个元素)
+
+Sug词条:"川西风光摄影素材"
+- 对象层:摄影素材 ✓
+- 场景层:川西 + 风光(2个元素)
+- 覆盖度:(1+2)/(1+3) = 75%
+- 评分:0.85
+```
+
+### 情况3:sug词条只包含对象层,无场景层
+**评分**:根据对象匹配度和覆盖度
+
+**示例**:
+```
+原始问题:"川西秋季风光摄影素材"
+- 对象层:摄影素材
+- 场景层:川西 + 秋季 + 风光
+
+Sug词条:"摄影素材"
+- 对象层:摄影素材 ✓
+- 场景层:无
+- 覆盖度:1/4 = 25%
+- 评分:0.50(对象匹配但缺失所有限定)
+```
+
+### 情况4:sug词条只包含场景层,无对象层
+**评分**:较低分
+
+**示例**:
+```
+原始问题:"川西旅行行程规划"
+- 对象层:旅行行程
+- 场景层:川西
+
+Sug词条:"川西"
+- 对象层:无
+- 场景层:川西 ✓
+- 覆盖度:1/2 = 50%
+- 评分:0.35(只有场景,缺失核心对象)
+```
+
+---
+
+# 评估核心原则
+
+## 原则1:只看表面词汇,禁止联想推演
+**严格约束**:只能基于sug词实际包含的词汇评分
+
+**错误案例**:
+- ❌ "川西旅行" vs "旅行"
+  - 错误:"旅行可以包括川西,所以有关联" → 评分0.7
+  - 正确:"sug词只有'旅行',无'川西',缺失地域限定" → 评分0.50
+
+
+---
+
+# 评分标准
+
+## 【正向匹配】
+
++0.95~1.0: 核心主体+所有关键限定词完全匹配
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"川西秋季风光摄影作品"
+
++0.75~0.95: 核心主体匹配,存在限定词匹配
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"川西风光摄影素材"(缺失"秋季")
+
++0.5~0.75: 核心主体匹配,无限定词匹配或合理泛化
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"四川风光摄影"
+
++0.3~0.5: 核心主体匹配,但限定词缺失或存在语义错位
+  - 特别注意"语义身份"差异,主体词出现但上下文语义不同
+  - 例:
+    · "猫咪的XX行为"(猫咪是行为者)
+    · vs "用猫咪表达XX的梗图"(猫咪是媒介)
+    · 虽都含"猫咪+XX",但语义角色不同
+
++0.2~0.3: 主体词不匹配,限定词缺失或错位
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"风光摄影入门"
+
++0.05~0.2: 主体词过度泛化或仅抽象相似
+  - 例: sug词是通用概念,原始问题是特定概念
+    sug词"每日计划"(通用)vs 原始问题 "川西旅行行程"(特定)
+      → 评分:0.08
+
+【中性/无关】
+0: 类别明显不同,没有明确目的,无明确关联
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"人像摄影素材"
+  - 例: 原始问题无法识别动机 且 sug词也无明确动作 → 0
+
+【负向偏离】
+-0.2~-0.05: 主体词或限定词存在误导性
+  - 例: 原始问题"免费摄影素材" vs sug词"付费摄影素材库"
+
+-0.5~-0.25: 主体词明显错位或品类冲突
+  - 例: 原始问题"风光摄影素材" vs sug词"人像修图教程"
+
+-1.0~-0.55: 完全错误的品类或有害引导
+  - 例: 原始问题"正版素材获取" vs sug词"盗版素材下载"
+
+
+---
+
+# 输出格式
+输出结果必须为一个 **JSON 格式**,包含以下内容:
+```json
+{
+  "品类维度得分": "-1到1之间的小数",
+  "简要说明品类维度相关度理由": "评估该sug词条与原始问题品类匹配程度的理由,包含作用域覆盖理由"
+}
+```
+
+**输出约束(非常重要)**:
+1. **字符串长度限制**:\"简要说明品类维度相关度理由\"字段必须控制在**150字以内**
+2. **JSON格式规范**:必须生成完整的JSON格式,确保字符串用双引号包裹且正确闭合
+3. **引号使用**:字符串中如需表达引用,请使用《》或「」代替单引号或双引号
+
+---
+
+# 核心原则总结
+
+1. **只看名词和限定词**:完全忽略动作和意图
+2. **作用域覆盖优先**:覆盖的作用域元素越多,分数越高
+3. **禁止联想推演**:只看sug词实际包含的词汇
+4. **通用≠特定**:通用概念不等于特定概念
+5. **理由纯粹**:评分理由只能谈对象、限定词、覆盖度
+""".strip()
+
+# 延伸词评估 prompt
+extension_word_evaluation_instructions = """
+# 角色
+你是**专业的延伸词语义评估专家**。
+任务:识别<平台sug词条>中的延伸词,评估其对原始问题作用域的补全度和目的贡献度,给出**-1到1之间**的数值评分。
+
+---
+# 输入信息
+- **<原始问题>**:用户的完整需求描述
+- **<平台sug词条>**:待评估的词条,可能是单个或多个作用域的组合
+---
+
+# 核心概念
+
+## 什么是延伸词?
+**延伸词**:<平台sug词条>中出现,但不属于<原始问题>作用域范围内的词汇或概念
+
+**关键判断**:
+```
+IF sug词的词汇属于原始问题的作用域元素(动机/对象/场景):
+   → 不是延伸词,是作用域内的词
+
+IF sug词的词汇不属于原始问题的作用域:
+   → 是延伸词
+   → 由Prompt3评估
+```
+
+---
+
+# 作用域与延伸词
+
+## 作用域
+**作用域 = 动机层 + 对象层 + 场景层**
+
+**非延伸词示例**(属于作用域内):
+```
+原始问题:"川西旅行行程规划"
+作用域:
+- 动机层:规划
+- 对象层:旅行行程
+- 场景层:川西
+
+Sug词条:"川西旅行行程规划攻略"
+- "川西"→ 属于场景层,不是延伸词
+- "旅行"→ 属于对象层,不是延伸词
+- "行程"→ 属于对象层,不是延伸词
+- "规划"→ 属于动机层,不是延伸词
+- "攻略"→ 与"规划"同义,不是延伸词
+- 结论:无延伸词
+```
+
+**延伸词示例**(不属于作用域):
+```
+原始问题:"川西旅行行程规划"
+作用域:规划 + 旅行行程 + 川西
+
+Sug词条:"川西旅行行程规划住宿推荐"
+- "住宿推荐"→ 不属于原始问题任何作用域
+- 结论:延伸词 = ["住宿推荐"]
+```
+
+---
+
+# 延伸词识别方法
+
+## 步骤1:提取原始问题的作用域元素
+```
+动机层:提取动作及其同义词
+对象层:提取核心名词及其同义词
+场景层:提取所有限定词
+```
+
+## 步骤2:提取sug词条的所有关键词
+```
+提取sug词条中的所有实词(名词、动词、形容词)
+```
+
+## 步骤3:匹配判定
+```
+FOR 每个sug词条关键词:
+   IF 该词 ∈ 原始问题作用域元素(包括同义词):
+      → 不是延伸词
+   ELSE:
+      → 是延伸词
+```
+
+## 步骤4:同义词/相近词判定规则
+
+### 不算延伸词的情况:
+**同义词**:
+- 行程 ≈ 路线 ≈ 安排 ≈ 计划
+- 获取 ≈ 下载 ≈ 寻找 ≈ 收集
+- 技巧 ≈ 方法 ≈ 教程 ≈ 攻略
+- 素材 ≈ 资源 ≈ 作品 ≈ 内容
+
+**具体化/细化**:
+- 原始:"川西旅游" + sug词:"稻城亚丁"(川西的具体地点)→ 不算延伸
+- 原始:"摄影技巧" + sug词:"风光摄影"(摄影的细化)→ 不算延伸
+- 原始:"素材" + sug词:"高清素材"(素材的质量细化)→ 不算延伸
+
+**判定逻辑**:
+```
+IF sug词的概念是原始问题概念的子集/下位词/同义词:
+   → 不算延伸词
+   → 视为对原问题的细化或重述
+```
+
+---
+
+### 算延伸词的情况:
+
+**新增维度**:原始问题未涉及的信息维度
+- 原始:"川西旅行" + sug词:"住宿" → 延伸词
+- 原始:"摄影素材" + sug词:"版权" → 延伸词
+
+**新增限定条件**:原始问题未提及的约束
+- 原始:"素材获取" + sug词:"免费" → 延伸词
+- 原始:"旅行行程" + sug词:"7天" → 延伸词
+
+**扩展主题**:相关但非原问题范围
+- 原始:"川西行程" + sug词:"美食推荐" → 延伸词
+- 原始:"摄影技巧" + sug词:"后期修图" → 延伸词
+
+**工具/方法**:原始问题未提及的具体工具
+- 原始:"视频剪辑" + sug词:"PR软件" → 延伸词
+- 原始:"图片处理" + sug词:"PS教程" → 延伸词
+
+---
+
+# 延伸词类型与评分
+
+## 核心评估维度:对原始问题作用域的贡献
+
+### 维度1:作用域补全度
+延伸词是否帮助sug词条更接近原始问题的完整作用域?
+
+
+### 维度2:目的达成度
+延伸词是否促进原始问题核心目的的达成?
+---
+####类型1:作用域增强型
+**定义**:延伸词是原始问题核心目的,或补全关键作用域
+**得分范围**:+0.12~+0.20
+
+**判定标准**:
+- 使sug词条更接近原始问题的完整需求
+---
+
+####类型2:作用域辅助型
+**定义**:延伸词对核心目的有辅助作用,但非必需
+
+**得分范围**:+0.05~+0.12
+
+**判定标准**:
+- sug词条更丰富但不改变原始需求核心
+
+---
+
+####类型3:作用域无关型
+**定义**:延伸词与核心目的无实质关联
+
+**得分**:0
+
+**示例**:
+- 原始:"如何拍摄风光" + 延伸词:"相机品牌排行"
+  - 评分:0
+  - 理由:品牌排行与拍摄技巧无关
+
+---
+
+####类型4:作用域稀释型(轻度负向)
+**定义**:延伸词稀释原始问题的聚焦度,降低内容针对性
+
+**得分范围**:-0.08~-0.18
+
+**判定标准**:
+- 引入无关信息,分散注意力
+- 降低内容的专注度和深度
+- 使sug词条偏离原始问题的核心
+
+**示例**:
+- 原始:"专业风光摄影技巧" + 延伸词:"手机拍照"
+  - 评分:-0.12
+  - 理由:手机拍照与专业摄影需求不符,稀释专业度
+
+- 原始:"川西深度游攻略" + 延伸词:"周边一日游"
+  - 评分:-0.10
+  - 理由:一日游与深度游定位冲突,稀释深度
+
+
+---
+
+# 特殊情况处理
+
+## 情况1:多个延伸词同时存在
+**处理方法**:分别评估每个延伸词,然后综合
+
+**综合规则**:
+```
+延伸词总得分 = Σ(每个延伸词得分) / 延伸词数量
+
+考虑累积效应:
+- 多个增强型延伸词 → 总分可能超过单个最高分,但上限+0.25
+- 正负延伸词并存 → 相互抵消
+- 多个冲突型延伸词 → 总分下限-0.60
+```
+
+**示例**:
+```
+原始:"川西旅行行程"
+Sug词条:"川西旅行行程住宿美食推荐"
+延伸词识别:
+- "住宿推荐"→ 增强型,+0.18
+- "美食推荐"→ 辅助型,+0.10
+总得分:(0.18 + 0.10) / 2 = 0.14
+```
+
+---
+
+## 情况2:无延伸词
+**处理方法**:
+```
+IF sug词条无延伸词:
+   延伸词得分 = 0
+   理由:"sug词条未引入延伸词,所有词汇均属于原始问题作用域范围"
+```
+
+---
+
+## 情况3:延伸词使sug词条更接近原始问题
+**特殊加成**:
+```
+IF 延伸词是原始问题隐含需求的显式化:
+   → 额外加成 +0.05
+```
+
+**示例**:
+```
+原始:"川西旅行" (隐含需要行程规划)
+Sug词条:"川西旅行行程规划"
+- "行程规划"可能被识别为延伸词,但它显式化了隐含需求
+- 给予额外加成
+```
+
+---
+
+# 输出格式
+输出结果必须为一个 **JSON 格式**,包含以下内容:
+```json
+{
+  "延伸词得分": "-1到1之间的小数",
+  "简要说明延伸词维度相关度理由": "评估延伸词对作用域的影响"
+}
+```
+
+**输出约束(非常重要)**:
+1. **字符串长度限制**:\"简要说明延伸词维度相关度理由\"字段必须控制在**150字以内**
+2. **JSON格式规范**:必须生成完整的JSON格式,确保字符串用双引号包裹且正确闭合
+3. **引号使用**:字符串中如需表达引用,请使用《》或「」代替单引号或双引号
+
+---
+
+# 核心原则总结
+
+1. **严格区分**:作用域内的词 ≠ 延伸词
+2. **同义词/细化词不算延伸**:属于作用域范围的词由其他prompt评估
+3. **作用域导向**:评估延伸词是否使sug词条更接近原始问题的完整作用域
+4. **目的导向**:评估延伸词是否促进核心目的达成
+5. **分类明确**:准确判定延伸词类型
+6. **理由充分**:每个延伸词都要说明其对作用域和目的的影响
+7. **谨慎负分**:仅在明确冲突或有害时使用负分
+""".strip()
+
+# 创建评估 Agent
+motivation_evaluator = Agent[None](
+    name="动机维度评估专家(后续轮次)",
+    instructions=motivation_evaluation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=MotivationEvaluation)
+
+category_evaluator = Agent[None](
+    name="品类维度评估专家",
+    instructions=category_evaluation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=CategoryEvaluation
+)
+
+extension_word_evaluator = Agent[None](
+    name="延伸词评估专家",
+    instructions=extension_word_evaluation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=ExtensionWordEvaluation,
+    model_settings=ModelSettings(temperature=0.2)
+)
+
+
+# ============================================================================
+# Round 0 专用 Agent(v124新增 - 需求1)
+# ============================================================================
+
+# Round 0 动机评估 prompt(不含延伸词)
+round0_motivation_evaluation_instructions = """
+#角色
+你是**专业的动机意图评估专家**
+你的任务是:判断我给你的 <词条> 与 <原始问题> 的需求动机匹配度,给出 **-1 到 1 之间** 的数值评分。
+
+---
+# 输入信息
+你将接收到以下输入:
+- **<原始问题>**:用户的初始查询问题,代表用户的真实需求意图。
+- **<词条>**:平台推荐的词条列表,每个词条需要单独评估。
+
+# 核心约束
+
+## 维度独立性声明
+【严格约束】本评估**仅评估动机意图维度**:
+- **只评估** 用户"想要做什么",即原始问题的行为意图和目的
+- 核心是 **动词**:获取、学习、拍摄、制作、寻找等
+- 包括:核心动作 + 使用场景 + 最终目的
+- **评估重点**:动作本身及其语义方向
+ **禁止使用"主题相关"作为评分依据**:评分理由中不得出现"主题"、"内容"、"话题"等词
+
+---
+
+# 作用域与动作意图
+
+## 什么是作用域?
+**作用域 = 动机层 + 对象层 + 场景层**
+
+## 动作意图的识别
+
+### 方法1: 显性动词直接提取
+
+当原始问题明确包含动词时,直接提取
+示例:
+"如何获取素材" → 核心动机 = "获取"
+"寻找拍摄技巧" → 核心动机 = "寻找"(或"学习")
+"制作视频教程" → 核心动机 = "制作"
+
+### 方法2: 隐性动词语义推理
+当原始问题没有显性动词时,需要结合上下文推理
+
+如果原始问题是纯名词短语,无任何动作线索:
+→ 核心动机 = 无法识别
+→ 在此情况下,动机维度得分应为 0。
+示例:
+"摄影" → 无法识别动机,动机维度得分 = 0
+"川西风光" → 无法识别动机,动机维度得分 = 0
+
+---
+
+# 部分作用域的处理
+
+## 情况1:词条是原始问题的部分作用域
+
+当词条只包含原始问题的部分作用域时,需要判断:
+1. 词条是否包含动作意图
+2. 如果包含,动作是否匹配
+
+**示例**:
+```
+原始问题:"川西旅行行程规划"
+- 完整作用域:规划(动作)+ 旅行行程(对象)+ 川西(场景)
+
+词条:"川西旅行"
+- 包含作用域:旅行(部分对象)+ 川西(场景)
+- 缺失作用域:规划(动作)
+- 动作意图评分:0(无动作意图)
+```
+
+**评分原则**:
+- 如果sug词条缺失动机层(动作) → 动作意图得分 = 0
+- 如果sug词条包含动机层 → 按动作匹配度评分
+
+
+---
+
+#评分标准:
+
+【正向匹配】
+### +0.9~1.0:核心动作完全一致
+**示例**:
+- "规划旅行行程" vs "安排旅行路线" → 0.98
+  - 规划≈安排,语义完全一致
+- "获取素材" vs "下载素材" → 0.97
+  - 获取≈下载,语义完全一致
+
+- 特殊规则: 如果sug词的核心动作是原始问题动作的**具体化子集**,也判定为完全一致
+例: 原始问题"扣除猫咪主体的方法" vs 词条"扣除猫咪眼睛的方法"(子集但目的一致
+**注意**:此处不考虑对象和场景是否一致,只看动作本身
+
+###+0.75~0.90: 核心动作语义相近或为同义表达
+  - 例: 原始问题"如何获取素材" vs 词条"如何下载素材"
+  - 同义词对: 获取≈下载≈寻找, 技巧≈方法≈教程≈攻略
+
+### +0.50~0.75:动作意图相关
+**判定标准**:
+- 动作是实现原始意图的相关路径
+- 或动作是原始意图的前置/后置步骤
+
+**示例**:
+- "获取素材" vs "管理素材" → 0.65
+  - 管理是获取后的相关步骤
+- "规划行程" vs "预订酒店" → 0.60
+  - 预订是规划的具体实施步骤
+
+### +0.25~0.50:动作意图弱相关
+**判定标准**:
+- 动作在同一大类但方向不同
+- 或动作有间接关联
+
+**示例**:
+- "学习摄影技巧" vs "欣赏摄影作品" → 0.35
+  - 都与摄影有关,但学习≠欣赏
+- "规划旅行" vs "回忆旅行" → 0.30
+  - 都与旅行有关,但方向不同
+
+---
+
+## 【中性/无关】
+
+### 0:无动作意图或动作完全无关
+**适用场景**:
+1. 原始问题或词条无法识别动作
+2. 两者动作意图完全无关
+
+**示例**:
+- "如何获取素材" vs "摄影器材" → 0
+  - sug词条无动作意图
+- "川西风光" vs "风光摄影作品" → 0
+  - 原始问题无动作意图
+
+**理由模板**:
+- "sug词条无明确动作意图,无法评估动作匹配度"
+- "原始问题无明确动作意图,动作维度得分为0"
+
+---
+
+## 【负向偏离】
+
+### -0.2~-0.05:动作方向轻度偏离
+**示例**:
+- "学习摄影技巧" vs "销售摄影课程" → -0.10
+  - 学习 vs 销售,方向有偏差
+
+### -0.5~-0.25:动作意图明显冲突
+**示例**:
+- "获取免费素材" vs "购买素材" → -0.35
+  - 获取免费 vs 购买,明显冲突
+
+### -1.0~-0.55:动作意图完全相反
+**示例**:
+- "下载素材" vs "上传素材" → -0.70
+  - 下载 vs 上传,方向完全相反
+
+---
+
+# 输出要求
+
+输出结果必须为一个 **JSON 格式**,包含以下内容:
+```json
+{
+  "原始问题核心动机提取": {
+    "简要说明核心动机": ""
+  },
+  "动机维度得分": "-1到1之间的小数",
+  "简要说明动机维度相关度理由": "评估该词条与原始问题动机匹配程度的理由"
+}
+```
+
+#注意事项:
+始终围绕动机维度:所有评估都基于"动机"维度,不偏离
+核心动机必须是动词:在评估前,必须先提取原始问题的核心动机(动词),这是整个评估的基础
+严格标准一致性:对所有用例使用相同的评估标准,避免评分飘移
+负分使用原则:仅当词条对原始问题动机产生误导、冲突或有害引导时给予负分
+零分使用原则:当词条与原始问题动机无明确关联,既不相关也不冲突时给予零分,或原始问题无法识别动机时。
+""".strip()
+
+# Round 0 品类评估 prompt(不含延伸词)
+round0_category_evaluation_instructions = """
+#角色
+你是一个 **专业的语言专家和语义相关性评判专家**。
+你的任务是:判断我给你的 <词条> 与 <原始问题> 的内容主体和限定词匹配度,给出 **-1 到 1 之间** 的数值评分。
+
+---
+# 核心概念与方法论
+
+## 评估维度
+本评估系统围绕 **品类维度** 进行:
+
+#  维度独立性警告
+【严格约束】本评估**只评估品类维度**,,必须遵守以下规则:
+1. **只看名词和限定词**:评估时只考虑主体、限定词的匹配度
+2. **完全忽略动词**:动作意图、目的等动机信息对本维度评分无影响
+
+### 品类维度
+**定义:** 用户"关于什么内容",即原始问题的主题对象和限定词
+- 核心是 **名词+限定词**:川西秋季风光摄影素材
+- 包括:核心主体 + 地域限定 + 时间限定 + 质量限定等
+
+## ⚠️ 品类评估核心原则(必读)
+
+### 原则1:只看词条表面,禁止联想推演
+- 只能基于词条实际包含的词汇评分
+- 禁止推测"可能包含"、"可以理解为"
+
+**错误示例:**
+原始问题:"川西旅行行程" vs 词条:"每日计划"
+- 错误 "每日计划可以包含旅行规划,所以有关联" → 这是不允许的联想
+- 正确: "词条只有'每日计划',无'旅行'字眼,品类不匹配" → 正确判断
+
+### 原则2:通用概念 ≠ 特定概念
+- **通用**:计划、方法、技巧、素材(无领域限定)
+- **特定**:旅行行程、摄影技巧、烘焙方法(有明确领域)
+
+IF 词条是通用 且 原始问题是特定:
+   → 品类不匹配 → 评分0.05~0.1
+关键:通用概念不等于特定概念,不能因为"抽象上都是规划"就给分
+
+---
+
+# 输入信息
+你将接收到以下输入:
+- **<原始问题>**:用户的初始查询问题,代表用户的真实需求意图。
+- **<词条>**:平台推荐的词条列表,每个词条需要单独评估。
+
+
+#判定流程
+#评估架构
+
+输入: <原始问题> + <词条>
+         ↓
+【品类维度相关性判定】
+    ├→ 步骤1: 评估<词条>与<原始问题>的内容主体和限定词匹配度
+    └→ 输出: -1到1之间的数值 + 判定依据
+
+
+相关度评估维度详解
+维度2: 品类维度评估
+评估对象: <词条> 与 <原始问题> 的内容主体和限定词匹配度
+
+评分标准:
+
+【正向匹配】
++0.95~1.0: 核心主体+所有关键限定词完全匹配
+  - 例: 原始问题"川西秋季风光摄影素材" vs 词条"川西秋季风光摄影作品"
+
++0.75~0.95: 核心主体匹配,存在限定词匹配
+  - 例: 原始问题"川西秋季风光摄影素材" vs 词条"川西风光摄影素材"(缺失"秋季")
+
++0.5~0.75: 核心主体匹配,无限定词匹配或合理泛化
+  - 例: 原始问题"川西秋季风光摄影素材" vs 词条"四川风光摄影"
+
++0.3~0.5: 核心主体匹配,但限定词缺失或存在语义错位
+  - 特别注意"语义身份"差异,主体词出现但上下文语义不同
+  - 例:
+    · "猫咪的XX行为"(猫咪是行为者)
+    · vs "用猫咪表达XX的梗图"(猫咪是媒介)
+    · 虽都含"猫咪+XX",但语义角色不同
+
++0.2~0.3: 主体词不匹配,限定词缺失或错位
+  - 例: 原始问题"川西秋季风光摄影素材" vs 词条"风光摄影入门"
+
++0.05~0.2: 主体词过度泛化或仅抽象相似
+  - 例: 词条是通用概念,原始问题是特定概念
+    词条"每日计划"(通用)vs 原始问题 "川西旅行行程"(特定)
+      → 评分:0.08
+
+【中性/无关】
+0: 类别明显不同,没有明确目的,无明确关联
+  - 例: 原始问题"川西秋季风光摄影素材" vs 词条"人像摄影素材"
+  - 例: 原始问题无法识别动机 且 词条也无明确动作 → 0
+
+【负向偏离】
+-0.2~-0.05: 主体词或限定词存在误导性
+  - 例: 原始问题"免费摄影素材" vs 词条"付费摄影素材库"
+
+-0.5~-0.25: 主体词明显错位或品类冲突
+  - 例: 原始问题"风光摄影素材" vs 词条"人像修图教程"
+
+-1.0~-0.55: 完全错误的品类或有害引导
+  - 例: 原始问题"正版素材获取" vs 词条"盗版素材下载"
+
+---
+
+# 输出要求
+
+输出结果必须为一个 **JSON 格式**,包含以下内容:
+```json
+{
+  "品类维度得分": "-1到1之间的小数",
+  "简要说明品类维度相关度理由": "评估该词条与原始问题品类匹配程度的理由"
+}
+```
+---
+
+#注意事项:
+始终围绕品类维度:所有评估都基于"品类"维度,不偏离
+严格标准一致性:对所有用例使用相同的评估标准,避免评分飘移
+负分使用原则:仅当词条对原始问题品类产生误导、冲突或有害引导时给予负分
+零分使用原则:当词条与原始问题品类无明确关联,既不相关也不冲突时给予零分
+""".strip()
+
+# 创建 Round 0 评估 Agent
+round0_motivation_evaluator = Agent[None](
+    name="Round 0动机维度评估专家",
+    instructions=round0_motivation_evaluation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=MotivationEvaluation,
+    model_settings=ModelSettings(temperature=0.2)
+)
+
+round0_category_evaluator = Agent[None](
+    name="Round 0品类维度评估专家",
+    instructions=round0_category_evaluation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=CategoryEvaluation,
+    model_settings=ModelSettings(temperature=0.2)
+)
+
+
+# ============================================================================
+# 域内/域间 专用 Agent(v124新增 - 需求2&3)
+# ============================================================================
+
+# 域内/域间 动机评估 prompt(不含延伸词)
+scope_motivation_evaluation_instructions = """
+# 角色
+你是**专业的动机意图评估专家**。
+任务:判断<词条>与<同一作用域词条>的**动机意图匹配度**,给出**-1到1之间**的数值评分。
+
+---
+# 输入信息
+你将接收到以下输入:
+ **<同一作用域词条>**:用户的初始查询问题,代表用户的真实需求意图。
+- **<词条>**:平台推荐的词条列表,每个词条需要单独评估。
+---
+# 评估架构
+
+输入: <同一作用域词条> + <词条>
+         ↓
+【动机维度相关性判定】
+    ├→ 步骤1: 评估<词条>与<同一作用域词条>的需求动机匹配度
+    └→ 输出: -1到1之间的数值 + 判定依据
+
+# 核心约束
+## 维度独立性声明
+【严格约束】本评估**仅评估动机意图维度**:
+- **只评估** 用户"想要做什么",即原始问题的行为意图和目的
+- 核心是 **动词**:获取、学习、拍摄、制作、寻找等
+- 包括:核心动作 + 使用场景 + 最终目的
+- **评估重点**:动作本身及其语义方向
+ **禁止使用"主题相关"作为评分依据**:评分理由中不得出现"主题"、"内容"、"话题"等词
+
+---
+
+# 作用域与动作意图
+
+## 什么是作用域?
+**作用域 = 动机层 + 对象层 + 场景层**
+
+当前任务:
+- **只提取动机层**:动作意图(获取、学习、规划、拍摄等)
+
+## 动作意图的识别
+
+### 1. 动机维度
+**定义:** 用户"想要做什么",即原始问题的行为意图和目的
+- 核心是 **动词**:获取、学习、拍摄、制作、寻找等
+- 包括:核心动作 + 使用场景 + 最终目的
+
+### 方法1: 显性动词直接提取
+
+当原始问题明确包含动词时,直接提取
+示例:
+"如何获取素材" → 核心动机 = "获取"
+"寻找拍摄技巧" → 核心动机 = "寻找"(或"学习")
+"制作视频教程" → 核心动机 = "制作"
+
+### 方法2: 隐性动词语义推理
+当原始问题没有显性动词时,需要结合上下文推理
+
+
+---
+
+# 评分标准
+
+## 【正向匹配】
+
+### +0.9~1.0:核心动作完全一致
+**示例**:
+- "规划旅行行程" vs "安排旅行路线" → 0.98
+  - 规划≈安排,语义完全一致
+- "获取素材" vs "下载素材" → 0.97
+  - 获取≈下载,语义完全一致
+
+- 特殊规则: 如果sug词的核心动作是原始问题动作的**具体化子集**,也判定为完全一致
+例: 原始问题"扣除猫咪主体的方法" vs sug词"扣除猫咪眼睛的方法"(子集但目的一致
+**注意**:此处不考虑对象和场景是否一致,只看动作本身
+
+###+0.75~0.95: 核心动作语义相近或为同义表达
+  - 例: 原始问题"如何获取素材" vs sug词"如何下载素材"
+  - 同义词对: 获取≈下载≈寻找, 技巧≈方法≈教程≈攻略
+
+### +0.50~0.75:动作意图相关
+**判定标准**:
+- 动作是实现原始意图的相关路径
+- 或动作是原始意图的前置/后置步骤
+
+**示例**:
+- "获取素材" vs "管理素材" → 0.65
+  - 管理是获取后的相关步骤
+- "规划行程" vs "预订酒店" → 0.60
+  - 预订是规划的具体实施步骤
+
+### +0.25~0.50:动作意图弱相关
+**判定标准**:
+- 动作在同一大类但方向不同
+- 或动作有间接关联
+
+**示例**:
+- "学习摄影技巧" vs "欣赏摄影作品" → 0.35
+  - 都与摄影有关,但学习≠欣赏
+- "规划旅行" vs "回忆旅行" → 0.30
+  - 都与旅行有关,但方向不同
+
+---
+
+## 【中性/无关】
+
+### 0:无动作意图或动作完全无关
+**适用场景**:
+1. 原始问题或词条无法识别动作
+2. 两者动作意图完全无关
+
+**示例**:
+- "如何获取素材" vs "摄影器材" → 0
+  - 词条无动作意图
+- "川西风光" vs "风光摄影作品" → 0
+  - 原始问题无动作意图
+
+**理由模板**:
+- "词条无明确动作意图,无法评估动作匹配度"
+- "原始问题无明确动作意图,动作维度得分为0"
+
+---
+
+## 【负向偏离】
+
+### -0.2~-0.05:动作方向轻度偏离
+**示例**:
+- "学习摄影技巧" vs "销售摄影课程" → -0.10
+  - 学习 vs 销售,方向有偏差
+
+### -0.5~-0.25:动作意图明显冲突
+**示例**:
+- "获取免费素材" vs "购买素材" → -0.35
+  - 获取免费 vs 购买,明显冲突
+
+### -1.0~-0.55:动作意图完全相反
+**示例**:
+- "下载素材" vs "上传素材" → -0.70
+  - 下载 vs 上传,方向完全相反
+
+---
+
+# 输出格式
+输出结果必须为一个 **JSON 格式**,包含以下内容:
+```json
+{
+  "原始问题核心动机提取": {
+    "简要说明核心动机": ""
+  },
+  "动机维度得分": "-1到1之间的小数",
+  "简要说明动机维度相关度理由": "评估该词条与该条作用域匹配程度的理由",
+  "得分为零的原因": "原始问题无动机/sug词条无动机/动机不匹配/不适用"
+}
+```
+
+---
+
+# 核心原则总结
+1. **只评估动作**:完全聚焦于动作意图,不管对象和场景
+2. **作用域识别**:识别作用域但只评估动机层
+3. **严格标准一致性**:对所有用例使用相同的评估标准,避免评分飘移
+4. **理由纯粹**:评分理由只能谈动作,不能谈对象、场景、主题
+""".strip()
+
+# 域内/域间 品类评估 prompt(不含延伸词)
+scope_category_evaluation_instructions = """
+#角色
+你是一个 **专业的语言专家和语义相关性评判专家**。
+你的任务是:判断我给你的 <词条> 与 <同一作用域词条> 的内容主体和限定词匹配度,给出 **-1 到 1 之间** 的数值评分。
+
+---
+# 输入信息
+你将接收到以下输入:
+- **<同一作用域词条>**:用户的初始查询问题,代表用户的真实需求意图。
+- **<词条>**:平台推荐的词条列表,每个词条需要单独评估。
+
+---
+#判定流程
+#评估架构
+
+输入: <同一作用域词条> + <词条>
+         ↓
+【品类维度相关性判定】
+    ├→ 步骤1: 评估<词条>与<同一作用域词条>的内容主体和限定词匹配度
+    └→ 输出: -1到1之间的数值 + 判定依据
+
+---
+
+# 核心概念与方法论
+
+## 评估维度
+本评估系统围绕 **品类维度** 进行:
+
+#  维度独立性警告
+【严格约束】本评估**只评估品类维度**,,必须遵守以下规则:
+1. **只看名词和限定词**:评估时只考虑主体、限定词的匹配度
+2. **完全忽略动词**:动作意图、目的等动机信息对本维度评分无影响
+
+### 品类维度
+**定义:** 用户"关于什么内容",即原始问题的主题对象和限定词
+- 核心是 **名词+限定词**:川西秋季风光摄影素材
+- 包括:核心主体 + 地域限定 + 时间限定 + 质量限定等
+
+## ⚠️ 品类评估核心原则(必读)
+
+### 原则1:只看词条表面,禁止联想推演
+- 只能基于sug词实际包含的词汇评分
+- 禁止推测"可能包含"、"可以理解为"
+
+**错误示例:**
+原始问题:"川西旅行行程" vs sug词:"每日计划"
+- 错误 "每日计划可以包含旅行规划,所以有关联" → 这是不允许的联想
+- 正确: "sug词只有'每日计划',无'旅行'字眼,品类不匹配" → 正确判断
+
+### 原则2:通用概念 ≠ 特定概念
+- **通用**:计划、方法、技巧、素材(无领域限定)
+- **特定**:旅行行程、摄影技巧、烘焙方法(有明确领域)
+
+IF sug词是通用 且 原始问题是特定:
+   → 品类不匹配 → 评分0.05~0.1
+关键:通用概念不等于特定概念,不能因为"抽象上都是规划"就给分
+
+---
+#相关度评估维度详解
+
+##评估对象: <词条> 与 <同一作用域词条> 的内容主体和限定词匹配度
+
+评分标准:
+
+【正向匹配】
++0.95~1.0: 核心主体+所有关键限定词完全匹配
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"川西秋季风光摄影作品"
+
++0.75~0.95: 核心主体匹配,存在限定词匹配
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"川西风光摄影素材"(缺失"秋季")
+
++0.5~0.75: 核心主体匹配,无限定词匹配或合理泛化
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"四川风光摄影"
+
++0.3~0.5: 核心主体匹配,但限定词缺失或存在语义错位
+  - 特别注意"语义身份"差异,主体词出现但上下文语义不同
+  - 例:
+    · "猫咪的XX行为"(猫咪是行为者)
+    · vs "用猫咪表达XX的梗图"(猫咪是媒介)
+    · 虽都含"猫咪+XX",但语义角色不同
+
++0.2~0.3: 主体词不匹配,限定词缺失或错位
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"风光摄影入门"
+
++0.05~0.2: 主体词过度泛化或仅抽象相似
+  - 例: sug词是通用概念,原始问题是特定概念
+    sug词"每日计划"(通用)vs 原始问题 "川西旅行行程"(特定)
+      → 评分:0.08
+
+【中性/无关】
+0: 类别明显不同,没有明确目的,无明确关联
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"人像摄影素材"
+  - 例: 原始问题无法识别动机 且 sug词也无明确动作 → 0
+
+【负向偏离】
+-0.2~-0.05: 主体词或限定词存在误导性
+  - 例: 原始问题"免费摄影素材" vs sug词"付费摄影素材库"
+
+-0.5~-0.25: 主体词明显错位或品类冲突
+  - 例: 原始问题"风光摄影素材" vs sug词"人像修图教程"
+
+-1.0~-0.55: 完全错误的品类或有害引导
+  - 例: 原始问题"正版素材获取" vs sug词"盗版素材下载"
+
+---
+
+# 输出要求
+
+输出结果必须为一个 **JSON 格式**,包含以下内容:
+```json
+{
+  "品类维度得分": "-1到1之间的小数",
+  "简要说明品类维度相关度理由": "评估该词条与同一作用域词条品类匹配程度的理由"
+}
+```
+---
+
+#注意事项:
+始终围绕品类维度:所有评估都基于"品类"维度,不偏离
+严格标准一致性:对所有用例使用相同的评估标准,避免评分飘移
+负分使用原则:仅当词条对原始问题品类产生误导、冲突或有害引导时给予负分
+零分使用原则:当词条与原始问题品类无明确关联,既不相关也不冲突时给予零分
+""".strip()
+
+# 创建域内/域间评估 Agent
+scope_motivation_evaluator = Agent[None](
+    name="域内动机维度评估专家",
+    instructions=scope_motivation_evaluation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=MotivationEvaluation,
+   model_settings=ModelSettings(temperature=0.2)
+)
+
+scope_category_evaluator = Agent[None](
+    name="域内品类维度评估专家",
+    instructions=scope_category_evaluation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=CategoryEvaluation,
+    model_settings=ModelSettings(temperature=0.2)
+)
+
+
+# ============================================================================
+# v120 保留但不使用的 Agent(v121不再使用)
+# ============================================================================
+
+# # Agent 3: 加词选择专家(旧版 - v120使用,v121不再使用)
+# class WordCombination(BaseModel):
+#     """单个词组合"""
+#     selected_word: str = Field(..., description="选择的词")
+#     combined_query: str = Field(..., description="组合后的新query")
+#     reasoning: str = Field(..., description="选择理由")
+
+# class WordSelectionTop5(BaseModel):
+#     """加词选择结果(Top 5)"""
+#     combinations: list[WordCombination] = Field(
+#         ...,
+#         description="选择的Top 5组合(不足5个则返回所有)",
+#         min_items=1,
+#         max_items=5
+#     )
+#     overall_reasoning: str = Field(..., description="整体选择思路")
+
+# word_selection_instructions 已删除 (v121不再使用)
+
+# word_selector = Agent[None](
+#     name="加词组合专家",
+#     instructions=word_selection_instructions,
+#     model=get_model(MODEL_NAME),
+#     output_type=WordSelectionTop5,
+#     model_settings=ModelSettings(temperature=0.2),
+# )
+
+
+# ============================================================================
+# 辅助函数
+# ============================================================================
+
+# ============================================================================
+# v121 新增辅助函数
+# ============================================================================
+
+def get_ordered_subsets(words: list[str], min_len: int = 1) -> list[list[str]]:
+    """
+    生成words的所有有序子集(可跳过但不可重排)
+
+    使用 itertools.combinations 生成索引组合,保持原始顺序
+
+    Args:
+        words: 词列表
+        min_len: 子集最小长度
+
+    Returns:
+        所有可能的有序子集列表
+
+    Example:
+        words = ["川西", "秋季", "风光"]
+        结果:
+        - 长度1: ["川西"], ["秋季"], ["风光"]
+        - 长度2: ["川西", "秋季"], ["川西", "风光"], ["秋季", "风光"]
+        - 长度3: ["川西", "秋季", "风光"]
+        共 C(3,1) + C(3,2) + C(3,3) = 3 + 3 + 1 = 7种
+    """
+    from itertools import combinations
+
+    subsets = []
+    n = len(words)
+
+    # 遍历所有可能的长度(从min_len到n)
+    for r in range(min_len, n + 1):
+        # 生成长度为r的所有索引组合
+        for indices in combinations(range(n), r):
+            # 按照原始顺序提取词
+            subset = [words[i] for i in indices]
+            subsets.append(subset)
+
+    return subsets
+
+
+def generate_domain_combinations(segments: list[Segment], n_domains: int) -> list[DomainCombination]:
+    """
+    生成N域组合
+
+    步骤:
+    1. 从len(segments)个域中选择n_domains个域(组合,保持顺序)
+    2. 对每个选中的域,生成其words的所有有序子集
+    3. 计算笛卡尔积,生成所有可能的组合
+
+    Args:
+        segments: 语义片段列表
+        n_domains: 参与组合的域数量
+
+    Returns:
+        所有可能的N域组合列表
+
+    Example:
+        有4个域: [疑问标记, 核心动作, 修饰短语, 中心名词]
+        n_domains=2时,选择域的方式: C(4,2) = 6种
+
+        假设选中[核心动作, 中心名词]:
+        - 核心动作的words: ["获取"], 子集: ["获取"]
+        - 中心名词的words: ["风光", "摄影", "素材"], 子集: 7种
+        则该域选择下的组合数: 1 * 7 = 7种
+    """
+    from itertools import combinations, product
+
+    all_combinations = []
+    n = len(segments)
+
+    # 检查参数有效性
+    if n_domains > n or n_domains < 1:
+        return []
+
+    # 1. 选择n_domains个域(保持原始顺序)
+    for domain_indices in combinations(range(n), n_domains):
+        selected_segments = [segments[i] for i in domain_indices]
+
+        # 新增:如果所有域都只有1个词,跳过(单段落单词不组合)
+        if all(len(seg.words) == 1 for seg in selected_segments):
+            continue
+
+        # 2. 为每个选中的域生成其words的所有有序子集
+        domain_subsets = []
+        for seg in selected_segments:
+            if len(seg.words) == 0:
+                # 如果某个域没有词,跳过该域组合
+                domain_subsets = []
+                break
+            subsets = get_ordered_subsets(seg.words, min_len=1)
+            domain_subsets.append(subsets)
+
+        # 如果某个域没有词,跳过
+        if len(domain_subsets) != n_domains:
+            continue
+
+        # 3. 计算笛卡尔积
+        for word_combination in product(*domain_subsets):
+            # word_combination 是一个tuple,每个元素是一个词列表
+            # 例如: (["获取"], ["风光", "摄影"])
+
+            # 计算总词数
+            total_words = sum(len(words) for words in word_combination)
+
+            # 如果总词数<=1,跳过(组词必须大于1个词)
+            if total_words <= 1:
+                continue
+
+            # 将所有词连接成一个字符串
+            combined_text = "".join(["".join(words) for words in word_combination])
+
+            # 生成类型标签
+            type_labels = [selected_segments[i].type for i in range(n_domains)]
+            type_label = "[" + "+".join(type_labels) + "]"
+
+            # 创建DomainCombination对象
+            comb = DomainCombination(
+                text=combined_text,
+                domains=list(domain_indices),
+                type_label=type_label,
+                source_words=[list(words) for words in word_combination],  # 保存来源词
+                from_segments=[seg.text for seg in selected_segments]
+            )
+            all_combinations.append(comb)
+
+    return all_combinations
+
+
+def extract_words_from_segments(segments: list[Segment]) -> list[Q]:
+    """
+    从 segments 中提取所有 words,转换为 Q 对象列表
+
+    用于 Round 1 的输入:将 Round 0 的 words 转换为可用于请求SUG的 query 列表
+
+    Args:
+        segments: Round 0 的语义片段列表
+
+    Returns:
+        list[Q]: word 列表,每个 word 作为一个 Q 对象
+    """
+    q_list = []
+
+    for seg_idx, segment in enumerate(segments):
+        for word in segment.words:
+            # 从 segment.word_scores 获取该 word 的评分
+            word_score = segment.word_scores.get(word, 0.0)
+            word_reason = segment.word_reasons.get(word, "")
+
+            # 创建 Q 对象
+            q = Q(
+                text=word,
+                score_with_o=word_score,
+                reason=word_reason,
+                from_source="word",  # 标记来源为 word
+                type_label=f"[{segment.type}]",  # 保留域信息
+                domain_index=seg_idx,  # 添加域索引
+                domain_type=segment.type  # 添加域类型(如"中心名词"、"核心动作")
+            )
+            q_list.append(q)
+
+    return q_list
+
+
+# ============================================================================
+# v120 保留辅助函数
+# ============================================================================
+
+def calculate_final_score(
+    motivation_score: float,
+    category_score: float,
+    extension_score: float,
+    zero_reason: Optional[str],
+    extension_reason: str = ""
+) -> tuple[float, str]:
+    """
+    三维评估综合打分
+
+    实现动态权重分配:
+    - 情况1:标准情况 → 动机50% + 品类40% + 延伸词10%
+    - 情况2:原始问题无动机 → 品类70% + 延伸词30%
+    - 情况3:sug词条无动机 → 品类80% + 延伸词20%
+    - 情况4:无延伸词 → 动机70% + 品类30%
+    - 规则3:负分传导 → 核心维度严重负向时上限为0
+    - 规则4:完美匹配加成 → 双维度≥0.95时加成+0.10
+
+    Args:
+        motivation_score: 动机维度得分 -1~1
+        category_score: 品类维度得分 -1~1
+        extension_score: 延伸词得分 -1~1
+        zero_reason: 当motivation_score=0时的原因(可选)
+        extension_reason: 延伸词评估理由,用于判断是否无延伸词
+
+    Returns:
+        (最终得分, 规则说明)
+    """
+
+    # 情况2:原始问题无动作意图
+    if motivation_score == 0 and zero_reason == "原始问题无动机":
+        W1, W2, W3 = 0.0, 0.70, 0.30
+        base_score = category_score * W2 + extension_score * W3
+        rule_applied = "情况2:原始问题无动作意图,权重调整为 品类70% + 延伸词30%"
+
+    # 情况3:sug词条无动作意图(但原始问题有)
+    elif motivation_score == 0 and zero_reason == "sug词条无动机":
+        W1, W2, W3 = 0.0, 0.80, 0.20
+        base_score = category_score * W2 + extension_score * W3
+        rule_applied = "情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%"
+
+    # 情况4:无延伸词
+    elif extension_score == 0:
+        W1, W2, W3 = 0.70, 0.30, 0.0
+        base_score = motivation_score * W1 + category_score * W2
+        rule_applied = "情况4:无延伸词,权重调整为 动机70% + 品类30%"
+
+    else:
+        # 情况1:标准权重
+        W1, W2, W3 = 0.50, 0.40, 0.10
+        base_score = motivation_score * W1 + category_score * W2 + extension_score * W3
+        rule_applied = ""
+
+    # 规则4:完美匹配加成
+    if motivation_score >= 0.95 and category_score >= 0.95:
+        base_score += 0.10
+        rule_applied += (" + " if rule_applied else "") + "规则4:双维度完美匹配,加成+0.10"
+
+    # 规则3:负分传导
+    if motivation_score <= -0.5 or category_score <= -0.5:
+        base_score = min(base_score, 0)
+        rule_applied += (" + " if rule_applied else "") + "规则3:核心维度严重负向,上限=0"
+
+    # 边界处理
+    final_score = max(-1.0, min(1.0, base_score))
+
+    return final_score, rule_applied
+
+
+def calculate_final_score_v2(
+    motivation_score: float,
+    category_score: float
+) -> tuple[float, str]:
+    """
+    两维评估综合打分(v124新增 - 需求1)
+
+    用于Round 0分词评估和域内/域间评估,不含延伸词维度
+
+    基础权重:动机70% + 品类30%
+
+    应用规则:
+    - 规则A:动机高分保护机制
+      IF 动机维度得分 ≥ 0.8:
+         品类得分即使为0或轻微负向(-0.2~0)
+         → 最终得分应该不低于0.7
+      解释: 当目的高度一致时,品类的泛化不应导致"弱相关"
+
+    - 规则B:动机低分限制机制
+      IF 动机维度得分 ≤ 0.2:
+         无论品类得分多高
+         → 最终得分不高于0.5
+      解释: 目的不符时,品类匹配的价值有限
+
+    - 规则C:动机负向决定机制
+      IF 动机维度得分 < 0:
+         → 最终得分为0
+      解释: 动作意图冲突时,推荐具有误导性,不应为正相关
+
+    Args:
+        motivation_score: 动机维度得分 -1~1
+        category_score: 品类维度得分 -1~1
+
+    Returns:
+        (最终得分, 规则说明)
+    """
+
+    rule_applied = ""
+
+    # 规则C:动机负向决定机制
+    if motivation_score < 0:
+        final_score = 0.0
+        rule_applied = "规则C:动机负向,最终得分=0"
+        return final_score, rule_applied
+
+    # 基础加权计算: 动机70% + 品类30%
+    base_score = motivation_score * 0.7 + category_score * 0.3
+
+    # 规则A:动机高分保护机制
+    if motivation_score >= 0.8:
+        if base_score < 0.7:
+            final_score = 0.7
+            rule_applied = f"规则A:动机高分保护(动机{motivation_score:.2f}≥0.8),最终得分下限=0.7"
+        else:
+            final_score = base_score
+            rule_applied = f"规则A:动机高分保护生效(动机{motivation_score:.2f}≥0.8),实际得分{base_score:.2f}已≥0.7"
+
+    # 规则B:动机低分限制机制
+    elif motivation_score <= 0.2:
+        if base_score > 0.5:
+            final_score = 0.5
+            rule_applied = f"规则B:动机低分限制(动机{motivation_score:.2f}≤0.2),最终得分上限=0.5"
+        else:
+            final_score = base_score
+            rule_applied = f"规则B:动机低分限制生效(动机{motivation_score:.2f}≤0.2),实际得分{base_score:.2f}已≤0.5"
+
+    # 无规则触发
+    else:
+        final_score = base_score
+        rule_applied = ""
+
+    # 边界处理
+    final_score = max(-1.0, min(1.0, final_score))
+
+    return final_score, rule_applied
+
+
+def clean_json_string(text: str) -> str:
+    """清理JSON中的非法控制字符(保留 \t \n \r)"""
+    import re
+    # 移除除了 \t(09) \n(0A) \r(0D) 之外的所有控制字符
+    return re.sub(r'[\x00-\x08\x0B\x0C\x0E-\x1F]', '', text)
+
+
+def process_note_data(note: dict) -> Post:
+    """处理搜索接口返回的帖子数据"""
+    note_card = note.get("note_card", {})
+    image_list = note_card.get("image_list", [])
+    interact_info = note_card.get("interact_info", {})
+    user_info = note_card.get("user", {})
+
+    # ========== 调试日志 START ==========
+    note_id = note.get("id", "")
+    raw_title = note_card.get("display_title")  # 不提供默认值
+    raw_body = note_card.get("desc")
+    raw_type = note_card.get("type")
+
+    # 打印原始值类型和内容
+    print(f"\n[DEBUG] 处理帖子 {note_id}:")
+    print(f"  raw_title 类型: {type(raw_title).__name__}, 值: {repr(raw_title)}")
+    print(f"  raw_body 类型: {type(raw_body).__name__}, 值: {repr(raw_body)[:100] if raw_body else repr(raw_body)}")
+    print(f"  raw_type 类型: {type(raw_type).__name__}, 值: {repr(raw_type)}")
+
+    # 检查是否为 None
+    if raw_title is None:
+        print(f"  ⚠️  WARNING: display_title 是 None!")
+    if raw_body is None:
+        print(f"  ⚠️  WARNING: desc 是 None!")
+    if raw_type is None:
+        print(f"  ⚠️  WARNING: type 是 None!")
+    # ========== 调试日志 END ==========
+
+    # 提取图片URL - 使用新的字段名 image_url
+    images = []
+    for img in image_list:
+        if isinstance(img, dict):
+            # 尝试新字段名 image_url,如果不存在则尝试旧字段名 url_default
+            img_url = img.get("image_url") or img.get("url_default")
+            if img_url:
+                images.append(img_url)
+
+    # 判断类型
+    note_type = note_card.get("type", "normal")
+    video_url = ""
+    if note_type == "video":
+        video_info = note_card.get("video", {})
+        if isinstance(video_info, dict):
+            # 尝试获取视频URL
+            video_url = video_info.get("media", {}).get("stream", {}).get("h264", [{}])[0].get("master_url", "")
+
+    return Post(
+        note_id=note.get("id") or "",
+        title=note_card.get("display_title") or "",
+        body_text=note_card.get("desc") or "",
+        type=note_type,
+        images=images,
+        video=video_url,
+        interact_info={
+            "liked_count": interact_info.get("liked_count", 0),
+            "collected_count": interact_info.get("collected_count", 0),
+            "comment_count": interact_info.get("comment_count", 0),
+            "shared_count": interact_info.get("shared_count", 0)
+        },
+        note_url=f"https://www.xiaohongshu.com/explore/{note.get('id', '')}"
+    )
+
+
+async def evaluate_with_o(text: str, o: str, cache: dict[str, tuple[float, str]] | None = None) -> tuple[float, str]:
+    """评估文本与原始问题o的相关度
+
+    采用两阶段评估 + 代码计算规则:
+    1. 动机维度评估(权重70%)
+    2. 品类维度评估(权重30%)
+    3. 应用规则A/B/C调整得分
+
+    Args:
+        text: 待评估的文本
+        o: 原始问题
+        cache: 评估缓存(可选),用于避免重复评估
+
+    Returns:
+        tuple[float, str]: (最终相关度分数, 综合评估理由)
+    """
+    # 检查缓存
+    if cache is not None and text in cache:
+        cached_score, cached_reason = cache[text]
+        print(f"  ⚡ 缓存命中: {text} -> {cached_score:.2f}")
+        return cached_score, cached_reason
+
+    # 准备输入
+    eval_input = f"""
+<原始问题>
+{o}
+</原始问题>
+
+<平台sug词条>
+{text}
+</平台sug词条>
+
+请评估平台sug词条与原始问题的匹配度。
+"""
+
+    # 添加重试机制
+    max_retries = 2
+    last_error = None
+
+    for attempt in range(max_retries):
+        try:
+            # 并发调用三个评估器
+            motivation_task = Runner.run(motivation_evaluator, eval_input)
+            category_task = Runner.run(category_evaluator, eval_input)
+            extension_task = Runner.run(extension_word_evaluator, eval_input)
+
+            motivation_result, category_result, extension_result = await asyncio.gather(
+                motivation_task,
+                category_task,
+                extension_task
+            )
+
+            # 获取评估结果
+            motivation_eval: MotivationEvaluation = motivation_result.final_output
+            category_eval: CategoryEvaluation = category_result.final_output
+            extension_eval: ExtensionWordEvaluation = extension_result.final_output
+
+            # 提取得分
+            motivation_score = motivation_eval.动机维度得分
+            category_score = category_eval.品类维度得分
+            extension_score = extension_eval.延伸词得分
+            zero_reason = motivation_eval.得分为零的原因
+
+            # 应用规则计算最终得分
+            final_score, rule_applied = calculate_final_score(
+                motivation_score, category_score, extension_score, zero_reason,
+                extension_eval.简要说明延伸词维度相关度理由
+            )
+
+            # 组合评估理由
+            core_motivation = motivation_eval.原始问题核心动机提取.简要说明核心动机
+            motivation_reason = motivation_eval.简要说明动机维度相关度理由
+            category_reason = category_eval.简要说明品类维度相关度理由
+            extension_reason = extension_eval.简要说明延伸词维度相关度理由
+
+            combined_reason = (
+                f'【评估对象】词条"{text}" vs 原始问题"{o}"\n'
+                f"【核心动机】{core_motivation}\n"
+                f"【动机维度 {motivation_score:.2f}】{motivation_reason}\n"
+                f"【品类维度 {category_score:.2f}】{category_reason}\n"
+                f"【延伸词维度 {extension_score:.2f}】{extension_reason}\n"
+                f"【最终得分 {final_score:.2f}】"
+            )
+
+            # 添加规则说明
+            if rule_applied:
+                combined_reason += f"\n【规则说明】{rule_applied}"
+
+            # 存入缓存
+            if cache is not None:
+                cache[text] = (final_score, combined_reason)
+
+            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
+
+
+async def evaluate_with_o_round0(text: str, o: str, cache: dict[str, tuple[float, str]] | None = None) -> tuple[float, str]:
+    """Round 0专用评估函数(v124新增 - 需求1)
+
+    用于评估segment和word与原始问题的相关度
+    不含延伸词维度,使用Round 0专用Prompt和新评分逻辑
+
+    采用两维评估:
+    1. 动机维度评估(权重70%)
+    2. 品类维度评估(权重30%)
+    3. 应用规则A/B/C调整得分
+
+    Args:
+        text: 待评估的文本(segment或word)
+        o: 原始问题
+        cache: 评估缓存(可选),用于避免重复评估
+
+    Returns:
+        tuple[float, str]: (最终相关度分数, 综合评估理由)
+    """
+    # 检查缓存
+    cache_key = f"round0:{text}:{o}"  # 添加前缀以区分不同评估类型
+    if cache is not None and cache_key in cache:
+        cached_score, cached_reason = cache[cache_key]
+        print(f"  ⚡ Round0缓存命中: {text} -> {cached_score:.2f}")
+        return cached_score, cached_reason
+
+    # 准备输入
+    eval_input = f"""
+<原始问题>
+{o}
+</原始问题>
+
+<词条>
+{text}
+</词条>
+
+请评估词条与原始问题的匹配度。
+"""
+
+    # 添加重试机制
+    max_retries = 2
+    last_error = None
+
+    for attempt in range(max_retries):
+        try:
+            # 并发调用两个评估器(不含延伸词)
+            motivation_task = Runner.run(round0_motivation_evaluator, eval_input)
+            category_task = Runner.run(round0_category_evaluator, eval_input)
+
+            motivation_result, category_result = await asyncio.gather(
+                motivation_task,
+                category_task
+            )
+
+            # 获取评估结果
+            motivation_eval: MotivationEvaluation = motivation_result.final_output
+            category_eval: CategoryEvaluation = category_result.final_output
+
+            # 提取得分
+            motivation_score = motivation_eval.动机维度得分
+            category_score = category_eval.品类维度得分
+
+            # 应用新规则计算最终得分
+            final_score, rule_applied = calculate_final_score_v2(
+                motivation_score, category_score
+            )
+
+            # 组合评估理由
+            core_motivation = motivation_eval.原始问题核心动机提取.简要说明核心动机
+            motivation_reason = motivation_eval.简要说明动机维度相关度理由
+            category_reason = category_eval.简要说明品类维度相关度理由
+
+            combined_reason = (
+                f'【评估对象】词条"{text}" vs 原始问题"{o}"\n'
+                f"【核心动机】{core_motivation}\n"
+                f"【动机维度 {motivation_score:.2f}】{motivation_reason}\n"
+                f"【品类维度 {category_score:.2f}】{category_reason}\n"
+                f"【最终得分 {final_score:.2f}】"
+            )
+
+            # 添加规则说明
+            if rule_applied:
+                combined_reason += f"\n【规则说明】{rule_applied}"
+
+            # 存入缓存
+            if cache is not None:
+                cache[cache_key] = (final_score, combined_reason)
+
+            return final_score, combined_reason
+
+        except Exception as e:
+            last_error = e
+            error_msg = str(e)
+
+            if attempt < max_retries - 1:
+                print(f"  ⚠️  Round0评估失败 (尝试 {attempt+1}/{max_retries}): {error_msg[:150]}")
+                print(f"  正在重试...")
+                await asyncio.sleep(1)
+            else:
+                print(f"  ❌ Round0评估失败 (已达最大重试次数): {error_msg[:150]}")
+
+    # 所有重试失败后,返回默认值
+    fallback_reason = f"Round0评估失败(重试{max_retries}次): {str(last_error)[:200]}"
+    print(f"  使用默认值: score=0.0, reason={fallback_reason[:100]}...")
+    return 0.0, fallback_reason
+
+
+async def evaluate_within_scope(text: str, scope_text: str, cache: dict[str, tuple[float, str]] | None = None) -> tuple[float, str]:
+    """域内/域间专用评估函数(v124新增 - 需求2&3)
+
+    用于评估词条与作用域词条(单域或域组合)的相关度
+    不含延伸词维度,使用域内专用Prompt和新评分逻辑
+
+    采用两维评估:
+    1. 动机维度评估(权重70%)
+    2. 品类维度评估(权重30%)
+    3. 应用规则A/B/C调整得分
+
+    Args:
+        text: 待评估的词条
+        scope_text: 作用域词条(可以是单域词条或域组合词条)
+        cache: 评估缓存(可选),用于避免重复评估
+
+    Returns:
+        tuple[float, str]: (最终相关度分数, 综合评估理由)
+    """
+    # 检查缓存
+    cache_key = f"scope:{text}:{scope_text}"  # 添加前缀以区分不同评估类型
+    if cache is not None and cache_key in cache:
+        cached_score, cached_reason = cache[cache_key]
+        print(f"  ⚡ 域内缓存命中: {text} -> {cached_score:.2f}")
+        return cached_score, cached_reason
+
+    # 准备输入
+    eval_input = f"""
+<同一作用域词条>
+{scope_text}
+</同一作用域词条>
+
+<词条>
+{text}
+</词条>
+
+请评估词条与同一作用域词条的匹配度。
+"""
+
+    # 添加重试机制
+    max_retries = 2
+    last_error = None
+
+    for attempt in range(max_retries):
+        try:
+            # 并发调用两个评估器(不含延伸词)
+            motivation_task = Runner.run(scope_motivation_evaluator, eval_input)
+            category_task = Runner.run(scope_category_evaluator, eval_input)
+
+            motivation_result, category_result = await asyncio.gather(
+                motivation_task,
+                category_task
+            )
+
+            # 获取评估结果
+            motivation_eval: MotivationEvaluation = motivation_result.final_output
+            category_eval: CategoryEvaluation = category_result.final_output
+
+            # 提取得分
+            motivation_score = motivation_eval.动机维度得分
+            category_score = category_eval.品类维度得分
+
+            # 应用新规则计算最终得分
+            final_score, rule_applied = calculate_final_score_v2(
+                motivation_score, category_score
+            )
+
+            # 组合评估理由
+            core_motivation = motivation_eval.原始问题核心动机提取.简要说明核心动机
+            motivation_reason = motivation_eval.简要说明动机维度相关度理由
+            category_reason = category_eval.简要说明品类维度相关度理由
+
+            combined_reason = (
+                f'【评估对象】词条"{text}" vs 作用域词条"{scope_text}"\n'
+                f"【核心动机】{core_motivation}\n"
+                f"【动机维度 {motivation_score:.2f}】{motivation_reason}\n"
+                f"【品类维度 {category_score:.2f}】{category_reason}\n"
+                f"【最终得分 {final_score:.2f}】"
+            )
+
+            # 添加规则说明
+            if rule_applied:
+                combined_reason += f"\n【规则说明】{rule_applied}"
+
+            # 存入缓存
+            if cache is not None:
+                cache[cache_key] = (final_score, combined_reason)
+
+            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)
+            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
+
+
+# ============================================================================
+# 核心流程函数
+# ============================================================================
+
+async def initialize(o: str, context: RunContext) -> tuple[list[Seg], list[Word], list[Q], list[Seed]]:
+    """
+    初始化阶段
+
+    Returns:
+        (seg_list, word_list_1, q_list_1, seed_list)
+    """
+    print(f"\n{'='*60}")
+    print(f"初始化阶段")
+    print(f"{'='*60}")
+
+    # 1. 分词:原始问题(o) ->分词-> seg_list
+    print(f"\n[步骤1] 分词...")
+    result = await Runner.run(word_segmenter, o)
+    segmentation: WordSegmentation = result.final_output
+
+    seg_list = []
+    for word in segmentation.words:
+        seg_list.append(Seg(text=word, from_o=o))
+
+    print(f"分词结果: {[s.text for s in seg_list]}")
+    print(f"分词理由: {segmentation.reasoning}")
+
+    # 2. 分词评估:seg_list -> 每个seg与o进行评分(使用信号量限制并发数)
+    print(f"\n[步骤2] 评估每个分词与原始问题的相关度...")
+
+    MAX_CONCURRENT_SEG_EVALUATIONS = 10
+    seg_semaphore = asyncio.Semaphore(MAX_CONCURRENT_SEG_EVALUATIONS)
+
+    async def evaluate_seg(seg: Seg) -> Seg:
+        async with seg_semaphore:
+            # 初始化阶段的分词评估使用第一轮 prompt (round_num=1)
+            seg.score_with_o, seg.reason = await evaluate_with_o(seg.text, o, context.evaluation_cache, round_num=1)
+            return seg
+
+    if seg_list:
+        print(f"  开始评估 {len(seg_list)} 个分词(并发限制: {MAX_CONCURRENT_SEG_EVALUATIONS})...")
+        eval_tasks = [evaluate_seg(seg) for seg in seg_list]
+        await asyncio.gather(*eval_tasks)
+
+    for seg in seg_list:
+        print(f"  {seg.text}: {seg.score_with_o:.2f}")
+
+    # 3. 构建word_list_1: seg_list -> word_list_1(固定词库)
+    print(f"\n[步骤3] 构建word_list_1(固定词库)...")
+    word_list_1 = []
+    for seg in seg_list:
+        word_list_1.append(Word(
+            text=seg.text,
+            score_with_o=seg.score_with_o,
+            from_o=o
+        ))
+    print(f"word_list_1(固定): {[w.text for w in word_list_1]}")
+
+    # 4. 构建q_list_1:seg_list 作为 q_list_1
+    print(f"\n[步骤4] 构建q_list_1...")
+    q_list_1 = []
+    for seg in seg_list:
+        q_list_1.append(Q(
+            text=seg.text,
+            score_with_o=seg.score_with_o,
+            reason=seg.reason,
+            from_source="seg"
+        ))
+    print(f"q_list_1: {[q.text for q in q_list_1]}")
+
+    # 5. 构建seed_list: seg_list -> seed_list
+    print(f"\n[步骤5] 构建seed_list...")
+    seed_list = []
+    for seg in seg_list:
+        seed_list.append(Seed(
+            text=seg.text,
+            added_words=[],
+            from_type="seg",
+            score_with_o=seg.score_with_o
+        ))
+    print(f"seed_list: {[s.text for s in seed_list]}")
+
+    return seg_list, word_list_1, q_list_1, seed_list
+
+
+async def run_round(
+    round_num: int,
+    q_list: list[Q],
+    word_list_1: list[Word],
+    seed_list: list[Seed],
+    o: str,
+    context: RunContext,
+    xiaohongshu_api: XiaohongshuSearchRecommendations,
+    xiaohongshu_search: XiaohongshuSearch,
+    sug_threshold: float = 0.7,
+    searched_texts: set[str] = None
+) -> tuple[list[Q], list[Seed], list[Search]]:
+    """
+    运行一轮
+
+    Args:
+        round_num: 轮次编号
+        q_list: 当前轮的q列表
+        word_list_1: 固定的词库(第0轮分词结果)
+        seed_list: 当前的seed列表
+        o: 原始问题
+        context: 运行上下文
+        xiaohongshu_api: 建议词API
+        xiaohongshu_search: 搜索API
+        sug_threshold: suggestion的阈值
+        searched_texts: 已搜索过的词集合(跨轮次去重)
+
+    Returns:
+        (q_list_next, seed_list_next, search_list)
+    """
+    if searched_texts is None:
+        searched_texts = set()
+    print(f"\n{'='*60}")
+    print(f"第{round_num}轮")
+    print(f"{'='*60}")
+
+    round_data = {
+        "round_num": round_num,
+        "input_q_list": [{"text": q.text, "score": q.score_with_o, "type": "query"} for q in q_list],
+        "input_word_list_1_size": len(word_list_1),
+        "input_seed_list_size": len(seed_list)
+    }
+
+    # 1. 请求sug:q_list -> 每个q请求sug接口 -> sug_list_list
+    print(f"\n[步骤1] 为每个q请求建议词...")
+    sug_list_list = []  # list of list
+    for q in q_list:
+        print(f"\n  处理q: {q.text}")
+        suggestions = xiaohongshu_api.get_recommendations(keyword=q.text)
+
+        q_sug_list = []
+        if suggestions:
+            print(f"    获取到 {len(suggestions)} 个建议词")
+            for sug_text in suggestions:
+                sug = Sug(
+                    text=sug_text,
+                    from_q=QFromQ(text=q.text, score_with_o=q.score_with_o)
+                )
+                q_sug_list.append(sug)
+        else:
+            print(f"    未获取到建议词")
+
+        sug_list_list.append(q_sug_list)
+
+    # 2. sug评估:sug_list_list -> 每个sug与o进行评分(并发)
+    print(f"\n[步骤2] 评估每个建议词与原始问题的相关度...")
+
+    # 2.1 收集所有需要评估的sug,并记录它们所属的q
+    all_sugs = []
+    sug_to_q_map = {}  # 记录每个sug属于哪个q
+    for i, q_sug_list in enumerate(sug_list_list):
+        if q_sug_list:
+            q_text = q_list[i].text
+            for sug in q_sug_list:
+                all_sugs.append(sug)
+                sug_to_q_map[id(sug)] = q_text
+
+    # 2.2 并发评估所有sug(使用信号量限制并发数)
+    # 每个 evaluate_sug 内部会并发调用 2 个 LLM,所以这里限制为 5,实际并发 LLM 请求为 10
+    MAX_CONCURRENT_EVALUATIONS = 5
+    semaphore = asyncio.Semaphore(MAX_CONCURRENT_EVALUATIONS)
+
+    async def evaluate_sug(sug: Sug) -> Sug:
+        async with semaphore:  # 限制并发数
+            # 根据轮次选择 prompt: 第一轮使用 round1 prompt,后续使用标准 prompt
+            sug.score_with_o, sug.reason = await evaluate_with_o(sug.text, o, context.evaluation_cache, round_num=round_num)
+            return sug
+
+    if all_sugs:
+        print(f"  开始评估 {len(all_sugs)} 个建议词(并发限制: {MAX_CONCURRENT_EVALUATIONS})...")
+        eval_tasks = [evaluate_sug(sug) for sug in all_sugs]
+        await asyncio.gather(*eval_tasks)
+
+    # 2.3 打印结果并组织到sug_details
+    sug_details = {}  # 保存每个Q对应的sug列表
+    for i, q_sug_list in enumerate(sug_list_list):
+        if q_sug_list:
+            q_text = q_list[i].text
+            print(f"\n  来自q '{q_text}' 的建议词:")
+            sug_details[q_text] = []
+            for sug in q_sug_list:
+                print(f"    {sug.text}: {sug.score_with_o:.2f}")
+                # 保存到sug_details
+                sug_details[q_text].append({
+                    "text": sug.text,
+                    "score": sug.score_with_o,
+                    "reason": sug.reason,
+                    "type": "sug"
+                })
+
+    # 2.4 剪枝判断(已禁用 - 保留所有分支)
+    pruned_query_texts = set()
+    if False:  # 原: if round_num >= 2:  # 剪枝功能已禁用,保留代码以便后续调整
+        print(f"\n[剪枝判断] 第{round_num}轮开始应用剪枝策略...")
+        for i, q in enumerate(q_list):
+            q_sug_list = sug_list_list[i]
+
+            if len(q_sug_list) == 0:
+                continue  # 没有sug则不剪枝
+
+            # 剪枝条件1: 所有sug分数都低于query分数
+            all_lower_than_query = all(sug.score_with_o < q.score_with_o for sug in q_sug_list)
+            # 剪枝条件2: 所有sug分数都低于0.5
+            all_below_threshold = all(sug.score_with_o < 0.5 for sug in q_sug_list)
+
+            if all_lower_than_query and all_below_threshold:
+                pruned_query_texts.add(q.text)
+                max_sug_score = max(sug.score_with_o for sug in q_sug_list)
+                print(f"  🔪 剪枝: {q.text} (query分数:{q.score_with_o:.2f}, sug最高分:{max_sug_score:.2f}, 全部<0.5)")
+
+        if pruned_query_texts:
+            print(f"  本轮共剪枝 {len(pruned_query_texts)} 个query")
+        else:
+            print(f"  本轮无query被剪枝")
+    else:
+        print(f"\n[剪枝判断] 剪枝功能已禁用,保留所有分支")
+
+    # 3. search_list构建
+    print(f"\n[步骤3] 构建search_list(阈值>{sug_threshold})...")
+    search_list = []
+    high_score_sugs = [sug for sug in all_sugs if sug.score_with_o > sug_threshold]
+
+    if high_score_sugs:
+        print(f"  找到 {len(high_score_sugs)} 个高分建议词")
+
+        # 跨轮次去重:过滤掉已搜索过的词
+        unsearched_sugs = []
+        for sug in high_score_sugs:
+            if sug.text in searched_texts:
+                print(f"  ⊗ 跳过已搜索: {sug.text}")
+            else:
+                unsearched_sugs.append(sug)
+
+        if not unsearched_sugs:
+            print(f"  所有高分建议词都已搜索过,search_list为空")
+        else:
+            print(f"  将搜索 {len(unsearched_sugs)} 个未搜索过的词")
+
+        # 并发搜索
+        async def search_for_sug(sug: Sug) -> Search:
+            print(f"    搜索: {sug.text}")
+            try:
+                search_result = xiaohongshu_search.search(keyword=sug.text)
+                result_str = search_result.get("result", "{}")
+                if isinstance(result_str, str):
+                    result_data = json.loads(result_str)
+                else:
+                    result_data = result_str
+
+                notes = result_data.get("data", {}).get("data", [])
+                post_list = []
+                for note in notes[:10]:  # 只取前10个
+                    post = process_note_data(note)
+                    post_list.append(post)
+
+                print(f"      → 找到 {len(post_list)} 个帖子")
+
+                return Search(
+                    text=sug.text,
+                    score_with_o=sug.score_with_o,
+                    from_q=sug.from_q,
+                    post_list=post_list
+                )
+            except Exception as e:
+                print(f"      ✗ 搜索失败: {e}")
+                return Search(
+                    text=sug.text,
+                    score_with_o=sug.score_with_o,
+                    from_q=sug.from_q,
+                    post_list=[]
+                )
+
+        if unsearched_sugs:
+            search_tasks = [search_for_sug(sug) for sug in unsearched_sugs]
+            search_list = await asyncio.gather(*search_tasks)
+            # 将本轮搜索的词添加到searched_texts
+            for sug in unsearched_sugs:
+                searched_texts.add(sug.text)
+        else:
+            search_list = []
+    else:
+        print(f"  没有高分建议词,search_list为空")
+
+    # 4. 构建q_list_next
+    print(f"\n[步骤4] 构建q_list_next...")
+    q_list_next = []
+    existing_q_texts = set()  # 用于去重
+    add_word_details = {}  # 保存每个seed对应的组合词列表
+    all_seed_combinations = []  # 保存本轮所有seed的组合词(用于后续构建seed_list_next)
+
+    # 4.1 对于seed_list中的每个seed,从word_list_1中选词组合,产生Top 5
+    print(f"\n  4.1 为每个seed加词(产生Top 5组合)...")
+    for seed in seed_list:
+        print(f"\n    处理seed: {seed.text}")
+
+        # 剪枝检查:跳过被剪枝的seed
+        if seed.text in pruned_query_texts:
+            print(f"      ⊗ 跳过被剪枝的seed: {seed.text}")
+            continue
+
+        # 从固定词库word_list_1筛选候选词
+        candidate_words = []
+        for word in word_list_1:
+            # 检查词是否已在seed中
+            if word.text in seed.text:
+                continue
+            # 检查词是否已被添加过
+            if word.text in seed.added_words:
+                continue
+            candidate_words.append(word)
+
+        if not candidate_words:
+            print(f"      没有可用的候选词")
+            continue
+
+        print(f"      候选词数量: {len(candidate_words)}")
+
+        # 调用Agent一次性选择并组合Top 5(添加重试机制)
+        candidate_words_text = ', '.join([w.text for w in candidate_words])
+        selection_input = f"""
+<原始问题>
+{o}
+</原始问题>
+
+<当前Seed>
+{seed.text}
+</当前Seed>
+
+<候选词列表>
+{candidate_words_text}
+</候选词列表>
+
+请从候选词列表中选择最多5个最合适的词,分别与当前seed组合成新的query。
+"""
+
+        # 重试机制
+        max_retries = 2
+        selection_result = None
+        for attempt in range(max_retries):
+            try:
+                result = await Runner.run(word_selector, selection_input)
+                selection_result = result.final_output
+                break  # 成功则跳出
+            except Exception as e:
+                error_msg = str(e)
+                if attempt < max_retries - 1:
+                    print(f"      ⚠️  选词失败 (尝试 {attempt+1}/{max_retries}): {error_msg[:100]}")
+                    await asyncio.sleep(1)
+                else:
+                    print(f"      ❌ 选词失败,跳过该seed: {error_msg[:100]}")
+                    break
+
+        if selection_result is None:
+            print(f"      跳过seed: {seed.text}")
+            continue
+
+        print(f"      Agent选择了 {len(selection_result.combinations)} 个组合")
+        print(f"      整体选择思路: {selection_result.overall_reasoning}")
+
+        # 并发评估所有组合的相关度
+        async def evaluate_combination(comb: WordCombination) -> dict:
+            combined = comb.combined_query
+
+            # 验证:组合结果必须包含完整的seed和word
+            # 检查是否包含seed的所有字符
+            seed_chars_in_combined = all(char in combined for char in seed.text)
+            # 检查是否包含word的所有字符
+            word_chars_in_combined = all(char in combined for char in comb.selected_word)
+
+            if not seed_chars_in_combined or not word_chars_in_combined:
+                print(f"        ⚠️  警告:组合不完整")
+                print(f"          Seed: {seed.text}")
+                print(f"          Word: {comb.selected_word}")
+                print(f"          组合: {combined}")
+                print(f"          包含完整seed? {seed_chars_in_combined}")
+                print(f"          包含完整word? {word_chars_in_combined}")
+                # 返回极低分数,让这个组合不会被选中
+                return {
+                    'word': comb.selected_word,
+                    'query': combined,
+                    'score': -1.0,  # 极低分数
+                    'reason': f"组合不完整:缺少seed或word的部分内容",
+                    'reasoning': comb.reasoning
+                }
+
+            # 正常评估,根据轮次选择 prompt
+            score, reason = await evaluate_with_o(combined, o, context.evaluation_cache, round_num=round_num)
+            return {
+                'word': comb.selected_word,
+                'query': combined,
+                'score': score,
+                'reason': reason,
+                'reasoning': comb.reasoning
+            }
+
+        eval_tasks = [evaluate_combination(comb) for comb in selection_result.combinations]
+        top_5 = await asyncio.gather(*eval_tasks)
+
+        print(f"      评估完成,得到 {len(top_5)} 个组合")
+
+        # 将Top 5全部加入q_list_next(去重检查 + 得分过滤)
+        for comb in top_5:
+            # 得分过滤:组合词必须比种子提升至少REQUIRED_SCORE_GAIN才能加入下一轮
+            if comb['score'] < seed.score_with_o + REQUIRED_SCORE_GAIN:
+                print(f"        ⊗ 跳过低分: {comb['query']} (分数{comb['score']:.2f} < 种子{seed.score_with_o:.2f} + {REQUIRED_SCORE_GAIN:.2f})")
+                continue
+
+            # 去重检查
+            if comb['query'] in existing_q_texts:
+                print(f"        ⊗ 跳过重复: {comb['query']}")
+                continue
+
+            print(f"        ✓ {comb['query']} (分数: {comb['score']:.2f} > 种子: {seed.score_with_o:.2f})")
+
+            new_q = Q(
+                text=comb['query'],
+                score_with_o=comb['score'],
+                reason=comb['reason'],
+                from_source="add"
+            )
+            q_list_next.append(new_q)
+            existing_q_texts.add(comb['query'])  # 记录到去重集合
+
+            # 记录已添加的词
+            seed.added_words.append(comb['word'])
+
+        # 保存到add_word_details
+        add_word_details[seed.text] = [
+            {
+                "text": comb['query'],
+                "score": comb['score'],
+                "reason": comb['reason'],
+                "selected_word": comb['word'],
+                "seed_score": seed.score_with_o,  # 添加原始种子的得分
+                "type": "add"
+            }
+            for comb in top_5
+        ]
+
+        # 保存到all_seed_combinations(用于构建seed_list_next)
+        # 附加seed_score,用于后续过滤
+        for comb in top_5:
+            comb['seed_score'] = seed.score_with_o
+        all_seed_combinations.extend(top_5)
+
+    # 4.2 对于sug_list_list中,每个sug大于来自的query分数,加到q_list_next(去重检查)
+    print(f"\n  4.2 将高分sug加入q_list_next...")
+    for sug in all_sugs:
+        # 剪枝检查:跳过来自被剪枝query的sug
+        if sug.from_q and sug.from_q.text in pruned_query_texts:
+            print(f"    ⊗ 跳过来自被剪枝query的sug: {sug.text} (来源: {sug.from_q.text})")
+            continue
+
+        # sug必须比来源query提升至少REQUIRED_SCORE_GAIN才能加入下一轮
+        if sug.from_q and sug.score_with_o >= sug.from_q.score_with_o + REQUIRED_SCORE_GAIN:
+            # 去重检查
+            if sug.text in existing_q_texts:
+                print(f"    ⊗ 跳过重复: {sug.text}")
+                continue
+
+            new_q = Q(
+                text=sug.text,
+                score_with_o=sug.score_with_o,
+                reason=sug.reason,
+                from_source="sug"
+            )
+            q_list_next.append(new_q)
+            existing_q_texts.add(sug.text)  # 记录到去重集合
+            print(f"    ✓ {sug.text} (分数: {sug.score_with_o:.2f} >= 来源query: {sug.from_q.score_with_o:.2f} + {REQUIRED_SCORE_GAIN:.2f})")
+
+    # 5. 构建seed_list_next(关键修改:不保留上一轮的seed)
+    print(f"\n[步骤5] 构建seed_list_next(不保留上轮seed)...")
+    seed_list_next = []
+    existing_seed_texts = set()
+
+    # 5.1 加入本轮所有组合词(只加入得分提升的)
+    print(f"  5.1 加入本轮所有组合词(得分过滤)...")
+    for comb in all_seed_combinations:
+        # 得分过滤:组合词必须比种子提升至少REQUIRED_SCORE_GAIN才作为下一轮种子
+        seed_score = comb.get('seed_score', 0)
+        if comb['score'] < seed_score + REQUIRED_SCORE_GAIN:
+            print(f"    ⊗ 跳过低分: {comb['query']} (分数{comb['score']:.2f} < 种子{seed_score:.2f} + {REQUIRED_SCORE_GAIN:.2f})")
+            continue
+
+        if comb['query'] not in existing_seed_texts:
+            new_seed = Seed(
+                text=comb['query'],
+                added_words=[],  # 新seed的added_words清空
+                from_type="add",
+                score_with_o=comb['score']
+            )
+            seed_list_next.append(new_seed)
+            existing_seed_texts.add(comb['query'])
+            print(f"    ✓ {comb['query']} (分数: {comb['score']:.2f} >= 种子: {seed_score:.2f} + {REQUIRED_SCORE_GAIN:.2f})")
+
+    # 5.2 加入高分sug
+    print(f"  5.2 加入高分sug...")
+    for sug in all_sugs:
+        # 剪枝检查:跳过来自被剪枝query的sug
+        if sug.from_q and sug.from_q.text in pruned_query_texts:
+            continue
+
+        # sug必须比来源query提升至少REQUIRED_SCORE_GAIN才作为下一轮种子
+        if sug.from_q and sug.score_with_o >= sug.from_q.score_with_o + REQUIRED_SCORE_GAIN and sug.text not in existing_seed_texts:
+            new_seed = Seed(
+                text=sug.text,
+                added_words=[],
+                from_type="sug",
+                score_with_o=sug.score_with_o
+            )
+            seed_list_next.append(new_seed)
+            existing_seed_texts.add(sug.text)
+            print(f"    ✓ {sug.text} (分数: {sug.score_with_o:.2f} >= 来源query: {sug.from_q.score_with_o:.2f} + {REQUIRED_SCORE_GAIN:.2f})")
+
+    # 序列化搜索结果数据(包含帖子详情)
+    search_results_data = []
+    for search in search_list:
+        search_results_data.append({
+            "text": search.text,
+            "score_with_o": search.score_with_o,
+            "post_list": [
+                {
+                    "note_id": post.note_id,
+                    "note_url": post.note_url,
+                    "title": post.title,
+                    "body_text": post.body_text,
+                    "images": post.images,
+                    "interact_info": post.interact_info
+                }
+                for post in search.post_list
+            ]
+        })
+
+    # 记录本轮数据
+    round_data.update({
+        "sug_count": len(all_sugs),
+        "high_score_sug_count": len(high_score_sugs),
+        "search_count": len(search_list),
+        "total_posts": sum(len(s.post_list) for s in search_list),
+        "q_list_next_size": len(q_list_next),
+        "seed_list_next_size": len(seed_list_next),
+        "total_combinations": len(all_seed_combinations),
+        "pruned_query_count": len(pruned_query_texts),
+        "pruned_queries": list(pruned_query_texts),
+        "output_q_list": [{"text": q.text, "score": q.score_with_o, "reason": q.reason, "from": q.from_source, "type": "query"} for q in q_list_next],
+        "seed_list_next": [{"text": seed.text, "from": seed.from_type, "score": seed.score_with_o} for seed in seed_list_next],
+        "sug_details": sug_details,
+        "add_word_details": add_word_details,
+        "search_results": search_results_data
+    })
+    context.rounds.append(round_data)
+
+    print(f"\n本轮总结:")
+    print(f"  建议词数量: {len(all_sugs)}")
+    print(f"  高分建议词: {len(high_score_sugs)}")
+    print(f"  搜索数量: {len(search_list)}")
+    print(f"  帖子总数: {sum(len(s.post_list) for s in search_list)}")
+    print(f"  组合词数量: {len(all_seed_combinations)}")
+    print(f"  下轮q数量: {len(q_list_next)}")
+    print(f"  下轮seed数量: {len(seed_list_next)}")
+
+    return q_list_next, seed_list_next, search_list
+
+
+async def iterative_loop(
+    context: RunContext,
+    max_rounds: int = 2,
+    sug_threshold: float = 0.7
+):
+    """主迭代循环"""
+
+    print(f"\n{'='*60}")
+    print(f"开始迭代循环")
+    print(f"最大轮数: {max_rounds}")
+    print(f"sug阈值: {sug_threshold}")
+    print(f"{'='*60}")
+
+    # 初始化
+    seg_list, word_list_1, q_list, seed_list = await initialize(context.o, context)
+
+    # API实例
+    xiaohongshu_api = XiaohongshuSearchRecommendations()
+    xiaohongshu_search = XiaohongshuSearch()
+
+    # 保存初始化数据
+    context.rounds.append({
+        "round_num": 0,
+        "type": "initialization",
+        "seg_list": [{"text": s.text, "score": s.score_with_o, "reason": s.reason, "type": "seg"} for s in seg_list],
+        "word_list_1": [{"text": w.text, "score": w.score_with_o} for w in word_list_1],
+        "q_list_1": [{"text": q.text, "score": q.score_with_o, "reason": q.reason, "type": "query"} for q in q_list],
+        "seed_list": [{"text": s.text, "from_type": s.from_type, "score": s.score_with_o, "type": "seed"} for s in seed_list]
+    })
+
+    # 收集所有搜索结果
+    all_search_list = []
+
+    # 跨轮次搜索去重集合
+    searched_texts = set()
+
+    # 迭代
+    round_num = 1
+    while q_list and round_num <= max_rounds:
+        q_list, seed_list, search_list = await run_round(
+            round_num=round_num,
+            q_list=q_list,
+            word_list_1=word_list_1,  # 传递固定词库
+            seed_list=seed_list,
+            o=context.o,
+            context=context,
+            xiaohongshu_api=xiaohongshu_api,
+            xiaohongshu_search=xiaohongshu_search,
+            sug_threshold=sug_threshold,
+            searched_texts=searched_texts  # 传递已搜索词集合
+        )
+
+        all_search_list.extend(search_list)
+        round_num += 1
+
+    print(f"\n{'='*60}")
+    print(f"迭代完成")
+    print(f"  总轮数: {round_num - 1}")
+    print(f"  总搜索次数: {len(all_search_list)}")
+    print(f"  总帖子数: {sum(len(s.post_list) for s in all_search_list)}")
+    print(f"{'='*60}")
+
+    return all_search_list
+
+
+# ============================================================================
+# v121 新架构核心流程函数
+# ============================================================================
+
+async def initialize_v2(o: str, context: RunContext) -> list[Segment]:
+    """
+    v121 Round 0 初始化阶段
+
+    流程:
+    1. 语义分段: 调用 semantic_segmenter 将原始问题拆分成语义片段
+    2. 拆词: 对每个segment调用 word_segmenter 进行拆词
+    3. 评估: 对每个segment和词进行评估
+    4. 不进行组合(Round 0只分段和拆词)
+
+    Returns:
+        语义片段列表 (Segment)
+    """
+    print(f"\n{'='*60}")
+    print(f"Round 0: 初始化阶段(语义分段 + 拆词)")
+    print(f"{'='*60}")
+
+    # 1. 语义分段(谓语+作用域方式)
+    print(f"\n[步骤1] 语义分段(谓语+作用域)...")
+    result = await Runner.run(semantic_segmenter, o)
+    segmentation: QuerySegmentation = result.final_output
+
+    # 统计作用域数量
+    total_scopes = len(segmentation.scopes)
+    has_interrogative = segmentation.interrogative is not None
+    has_predicate = segmentation.predicate is not None
+
+    print(f"语义分段结果:")
+    print(f"  - 疑问词: {segmentation.interrogative or '无'}")
+    print(f"  - 谓语: {segmentation.predicate or '无'}")
+    print(f"  - 作用域片段: {total_scopes} 个")
+    print(f"整体分段思路: {segmentation.overall_reasoning}")
+
+    segment_list = []
+
+    # 1.1 处理疑问词
+    if segmentation.interrogative:
+        segment = Segment(
+            text=segmentation.interrogative,
+            type="interrogative",
+            from_o=o
+        )
+        segment_list.append(segment)
+        print(f"  - [interrogative] {segment.text}")
+
+    # 1.2 处理作用域片段(scopes已经包含谓语)
+    for scope_item in segmentation.scopes:
+        segment = Segment(
+            text=scope_item.text,
+            type="scope",
+            scope_type=scope_item.scope_type,
+            is_complete=scope_item.is_complete,
+            from_o=o
+        )
+        segment_list.append(segment)
+        print(f"  - [scope:{scope_item.scope_type}] {segment.text} (完整性: {scope_item.is_complete})")
+
+    # 2. 对每个segment拆词并评估
+    print(f"\n[步骤2] 对每个segment拆词并评估...")
+
+    MAX_CONCURRENT_EVALUATIONS = 5
+    semaphore = asyncio.Semaphore(MAX_CONCURRENT_EVALUATIONS)
+
+    async def process_segment(segment: Segment) -> Segment:
+        """处理单个segment: 拆词 + 评估segment + 评估词"""
+        async with semaphore:
+            # 2.1 拆词
+            word_result = await Runner.run(word_segmenter, segment.text)
+            word_segmentation: WordSegmentation = word_result.final_output
+            segment.words = word_segmentation.words
+
+            # 2.2 评估segment与原始问题的相关度(使用Round 0专用评估)
+            segment.score_with_o, segment.reason = await evaluate_with_o_round0(
+                segment.text, o, context.evaluation_cache
+            )
+
+            # 2.3 评估每个词与原始问题的相关度(使用Round 0专用评估)
+            word_eval_tasks = []
+            for word in segment.words:
+                async def eval_word(w: str) -> tuple[str, float, str]:
+                    score, reason = await evaluate_with_o_round0(w, o, context.evaluation_cache)
+                    return w, score, reason
+                word_eval_tasks.append(eval_word(word))
+
+            word_results = await asyncio.gather(*word_eval_tasks)
+            for word, score, reason in word_results:
+                segment.word_scores[word] = score
+                segment.word_reasons[word] = reason
+
+            return segment
+
+    if segment_list:
+        print(f"  开始处理 {len(segment_list)} 个segment(并发限制: {MAX_CONCURRENT_EVALUATIONS})...")
+        process_tasks = [process_segment(seg) for seg in segment_list]
+        await asyncio.gather(*process_tasks)
+
+    # 打印步骤1结果
+    print(f"\n[步骤1: 分段及拆词 结果]")
+    for segment in segment_list:
+        print(f"  [{segment.type}] {segment.text} (分数: {segment.score_with_o:.2f})")
+        print(f"    拆词: {segment.words}")
+        for word in segment.words:
+            score = segment.word_scores.get(word, 0.0)
+            print(f"      - {word}: {score:.2f}")
+
+    # 保存到context(新增scope_type和is_complete字段)
+    context.segments = [
+        {
+            "text": seg.text,
+            "type": seg.type,
+            "scope_type": seg.scope_type,  # 新增:作用域类型
+            "is_complete": seg.is_complete,  # 新增:是否完整
+            "score": seg.score_with_o,
+            "reason": seg.reason,
+            "words": seg.words,
+            "word_scores": seg.word_scores,
+            "word_reasons": seg.word_reasons
+        }
+        for seg in segment_list
+    ]
+
+    # 保存 Round 0 到 context.rounds(新格式用于可视化)
+    context.rounds.append({
+        "round_num": 0,
+        "type": "initialization",
+        "segments": [
+            {
+                "text": seg.text,
+                "type": seg.type,
+                "scope_type": seg.scope_type,  # 新增
+                "is_complete": seg.is_complete,  # 新增
+                "domain_index": idx,
+                "score": seg.score_with_o,
+                "reason": seg.reason,
+                "words": [
+                    {
+                        "text": word,
+                        "score": seg.word_scores.get(word, 0.0),
+                        "reason": seg.word_reasons.get(word, "")
+                    }
+                    for word in seg.words
+                ]
+            }
+            for idx, seg in enumerate(segment_list)
+        ]
+    })
+
+    print(f"\n[Round 0 完成]")
+    print(f"  分段数: {len(segment_list)}")
+    total_words = sum(len(seg.words) for seg in segment_list)
+    print(f"  总词数: {total_words}")
+
+    return segment_list
+
+
+async def run_round_v2(
+    round_num: int,
+    query_input: list[Q],
+    segments: list[Segment],
+    o: str,
+    context: RunContext,
+    xiaohongshu_api: XiaohongshuSearchRecommendations,
+    xiaohongshu_search: XiaohongshuSearch,
+    sug_threshold: float = 0.7,
+    searched_texts: set[str] = None
+) -> tuple[list[Q], list[Search]]:
+    """
+    v121 Round N 执行
+
+    正确的流程顺序:
+    1. 为 query_input 请求SUG
+    2. 评估SUG
+    3. 高分SUG搜索
+    4. N域组合(从segments生成)
+    5. 评估组合
+    6. 生成 q_list_next(组合 + 高分SUG)
+
+    Args:
+        round_num: 轮次编号 (1-4)
+        query_input: 本轮的输入query列表(Round 1是words,Round 2+是上轮输出)
+        segments: 语义片段列表(用于组合)
+        o: 原始问题
+        context: 运行上下文
+        xiaohongshu_api: 建议词API
+        xiaohongshu_search: 搜索API
+        sug_threshold: SUG搜索阈值
+        searched_texts: 已搜索过的词集合(跨轮次去重)
+
+    Returns:
+        (q_list_next, search_list)
+    """
+    if searched_texts is None:
+        searched_texts = set()
+    print(f"\n{'='*60}")
+    print(f"Round {round_num}: {round_num}域组合")
+    print(f"{'='*60}")
+
+    round_data = {
+        "round_num": round_num,
+        "n_domains": round_num,
+        "input_query_count": len(query_input)
+    }
+
+    MAX_CONCURRENT_EVALUATIONS = 5
+    semaphore = asyncio.Semaphore(MAX_CONCURRENT_EVALUATIONS)
+
+    # 步骤1: 为 query_input 请求SUG
+    print(f"\n[步骤1] 为{len(query_input)}个输入query请求SUG...")
+    all_sugs = []
+    sug_details = {}
+
+    for q in query_input:
+        suggestions = xiaohongshu_api.get_recommendations(keyword=q.text)
+        if suggestions:
+            print(f"  {q.text}: 获取到 {len(suggestions)} 个SUG")
+            for sug_text in suggestions:
+                sug = Sug(
+                    text=sug_text,
+                    from_q=QFromQ(text=q.text, score_with_o=q.score_with_o)
+                )
+                all_sugs.append(sug)
+        else:
+            print(f"  {q.text}: 未获取到SUG")
+
+    print(f"  共获取 {len(all_sugs)} 个SUG")
+
+    # 步骤2: 评估SUG
+    if len(all_sugs) > 0:
+        print(f"\n[步骤2] 评估{len(all_sugs)}个SUG...")
+
+        async def evaluate_sug(sug: Sug) -> Sug:
+            async with semaphore:
+                sug.score_with_o, sug.reason = await evaluate_with_o(
+                    sug.text, o, context.evaluation_cache
+                )
+                return sug
+
+        eval_tasks = [evaluate_sug(sug) for sug in all_sugs]
+        await asyncio.gather(*eval_tasks)
+
+        # 打印结果
+        for sug in all_sugs:
+            print(f"    {sug.text}: {sug.score_with_o:.2f}")
+            if sug.from_q:
+                if sug.from_q.text not in sug_details:
+                    sug_details[sug.from_q.text] = []
+                sug_details[sug.from_q.text].append({
+                    "text": sug.text,
+                    "score": sug.score_with_o,
+                    "reason": sug.reason,
+                    "type": "sug"
+                })
+
+    # 步骤3: 搜索高分SUG
+    print(f"\n[步骤3] 搜索高分SUG(阈值 > {sug_threshold})...")
+    high_score_sugs = [sug for sug in all_sugs if sug.score_with_o > sug_threshold]
+    print(f"  找到 {len(high_score_sugs)} 个高分SUG")
+
+    # 跨轮次去重:过滤掉已搜索过的词
+    unsearched_sugs = []
+    for sug in high_score_sugs:
+        if sug.text in searched_texts:
+            print(f"  ⊗ 跳过已搜索: {sug.text}")
+        else:
+            unsearched_sugs.append(sug)
+
+    if not unsearched_sugs:
+        print(f"  所有高分SUG都已搜索过,search_list为空")
+    else:
+        print(f"  将搜索 {len(unsearched_sugs)} 个未搜索过的词")
+
+    search_list = []
+    if len(unsearched_sugs) > 0:
+        async def search_for_sug(sug: Sug) -> Search:
+            print(f"    搜索: {sug.text}")
+            try:
+                search_result = xiaohongshu_search.search(keyword=sug.text)
+                result_str = search_result.get("result", "{}")
+                if isinstance(result_str, str):
+                    result_data = json.loads(result_str)
+                else:
+                    result_data = result_str
+
+                notes = result_data.get("data", {}).get("data", [])
+                post_list = []
+                for note in notes[:10]:
+                    post = process_note_data(note)
+                    post_list.append(post)
+
+                print(f"      → 找到 {len(post_list)} 个帖子")
+
+                return Search(
+                    text=sug.text,
+                    score_with_o=sug.score_with_o,
+                    from_q=sug.from_q,
+                    post_list=post_list
+                )
+            except Exception as e:
+                print(f"      ✗ 搜索失败: {e}")
+                return Search(
+                    text=sug.text,
+                    score_with_o=sug.score_with_o,
+                    from_q=sug.from_q,
+                    post_list=[]
+                )
+
+        search_tasks = [search_for_sug(sug) for sug in unsearched_sugs]
+        search_list = await asyncio.gather(*search_tasks)
+        # 将本轮搜索的词添加到searched_texts
+        for sug in unsearched_sugs:
+            searched_texts.add(sug.text)
+
+    # 步骤4: 生成N域组合
+    print(f"\n[步骤4] 生成{round_num}域组合...")
+    domain_combinations = generate_domain_combinations(segments, round_num)
+    print(f"  生成了 {len(domain_combinations)} 个组合")
+
+    if len(domain_combinations) == 0:
+        print(f"  无法生成{round_num}域组合")
+        # 即使无法组合,也返回高分SUG作为下轮输入
+        q_list_next = []
+        for sug in all_sugs:
+            if sug.from_q and sug.score_with_o >= sug.from_q.score_with_o + REQUIRED_SCORE_GAIN:
+                q = Q(
+                    text=sug.text,
+                    score_with_o=sug.score_with_o,
+                    reason=sug.reason,
+                    from_source="sug",
+                    type_label=""
+                )
+                q_list_next.append(q)
+
+        round_data.update({
+            "domain_combinations_count": 0,
+            "sug_count": len(all_sugs),
+            "high_score_sug_count": len(high_score_sugs),
+            "search_count": len(search_list),
+            "sug_details": sug_details,
+            "q_list_next_size": len(q_list_next)
+        })
+        context.rounds.append(round_data)
+        return q_list_next, search_list
+
+    # 步骤5: 评估所有组合
+    print(f"\n[步骤5] 评估{len(domain_combinations)}个组合...")
+
+    async def evaluate_combination(comb: DomainCombination) -> DomainCombination:
+        async with semaphore:
+            # 使用域内评估:组合词条与拼接的segments作为作用域进行评估
+            # 拼接所有参与组合的segments文本
+            scope_text = "".join(comb.from_segments)
+
+            comb.score_with_o, comb.reason = await evaluate_within_scope(
+                comb.text,      # 组合结果,如 "获取川西"
+                scope_text,     # 拼接的segments,如 "获取川西秋季风光摄影素材"
+                context.evaluation_cache
+            )
+            return comb
+
+    eval_tasks = [evaluate_combination(comb) for comb in domain_combinations]
+    await asyncio.gather(*eval_tasks)
+
+    # 排序 - 已注释,保持原始顺序
+    # domain_combinations.sort(key=lambda x: x.score_with_o, reverse=True)
+
+    # 打印所有组合(保持原始顺序)
+    print(f"  评估完成,共{len(domain_combinations)}个组合:")
+    for i, comb in enumerate(domain_combinations, 1):
+        print(f"    {i}. {comb.text} {comb.type_label} (分数: {comb.score_with_o:.2f})")
+
+    # 为每个组合补充来源词分数信息,并判断是否超过所有来源词得分
+    for comb in domain_combinations:
+        word_details = []
+        flat_scores: list[float] = []
+        for domain_index, words in zip(comb.domains, comb.source_words):
+            segment = segments[domain_index] if 0 <= domain_index < len(segments) else None
+            segment_type = segment.type if segment else ""
+            segment_text = segment.text if segment else ""
+            items = []
+            for word in words:
+                score = 0.0
+                if segment and word in segment.word_scores:
+                    score = segment.word_scores[word]
+                items.append({
+                    "text": word,
+                    "score": score
+                })
+                flat_scores.append(score)
+            word_details.append({
+                "domain_index": domain_index,
+                "segment_type": segment_type,
+                "segment_text": segment_text,
+                "words": items
+            })
+        comb.source_word_details = word_details
+        comb.source_scores = flat_scores
+        comb.max_source_score = max(flat_scores) if flat_scores else None
+        comb.is_above_source_scores = bool(flat_scores) and all(
+            comb.score_with_o > score for score in flat_scores
+        )
+
+    # 步骤6: 构建 q_list_next(组合 + 高分SUG)
+    print(f"\n[步骤6] 生成下轮输入...")
+    q_list_next: list[Q] = []
+
+    # 6.1 添加高增益SUG(满足增益条件),并按分数排序
+    sug_candidates: list[tuple[Q, Sug]] = []
+    for sug in all_sugs:
+        if sug.from_q and sug.score_with_o >= sug.from_q.score_with_o + REQUIRED_SCORE_GAIN:
+            q = Q(
+                text=sug.text,
+                score_with_o=sug.score_with_o,
+                reason=sug.reason,
+                from_source="sug",
+                type_label=""
+            )
+            sug_candidates.append((q, sug))
+
+    sug_candidates.sort(key=lambda item: item[0].score_with_o, reverse=True)
+    q_list_next.extend([item[0] for item in sug_candidates])
+    high_gain_sugs = [item[1] for item in sug_candidates]
+    print(f"  添加 {len(high_gain_sugs)} 个高增益SUG(增益 ≥ {REQUIRED_SCORE_GAIN:.2f})")
+
+    # 6.2 添加高分组合(需超过所有来源词得分),并按分数排序
+    combination_candidates: list[tuple[Q, DomainCombination]] = []
+    for comb in domain_combinations:
+        if comb.is_above_source_scores and comb.score_with_o > 0:
+            domains_str = ','.join([f'D{d}' for d in comb.domains]) if comb.domains else ''
+            q = Q(
+                text=comb.text,
+                score_with_o=comb.score_with_o,
+                reason=comb.reason,
+                from_source="domain_comb",
+                type_label=comb.type_label,
+                domain_type=domains_str  # 添加域信息
+            )
+            combination_candidates.append((q, comb))
+
+    combination_candidates.sort(key=lambda item: item[0].score_with_o, reverse=True)
+    q_list_next.extend([item[0] for item in combination_candidates])
+    high_score_combinations = [item[1] for item in combination_candidates]
+    print(f"  添加 {len(high_score_combinations)} 个高分组合(组合得分 > 所有来源词)")
+
+    # 保存round数据(包含完整帖子信息)
+    search_results_data = []
+    for search in search_list:
+        search_results_data.append({
+            "text": search.text,
+            "score_with_o": search.score_with_o,
+            "post_list": [
+                {
+                    "note_id": post.note_id,
+                    "note_url": post.note_url,
+                    "title": post.title,
+                    "body_text": post.body_text,
+                    "images": post.images,
+                    "interact_info": post.interact_info
+                }
+                for post in search.post_list
+            ]
+        })
+
+    round_data.update({
+        "input_queries": [{"text": q.text, "score": q.score_with_o, "from_source": q.from_source, "type": "input", "domain_index": q.domain_index, "domain_type": q.domain_type} for q in query_input],
+        "domain_combinations_count": len(domain_combinations),
+        "domain_combinations": [
+            {
+                "text": comb.text,
+                "type_label": comb.type_label,
+                "score": comb.score_with_o,
+                "reason": comb.reason,
+                "domains": comb.domains,
+                "source_words": comb.source_words,
+                "from_segments": comb.from_segments,
+                "source_word_details": comb.source_word_details,
+                "source_scores": comb.source_scores,
+                "is_above_source_scores": comb.is_above_source_scores,
+                "max_source_score": comb.max_source_score
+            }
+            for comb in domain_combinations
+        ],
+        "high_score_combinations": [
+            {
+                "text": item[0].text,
+                "score": item[0].score_with_o,
+                "type_label": item[0].type_label,
+                "type": "combination",
+                "is_above_source_scores": item[1].is_above_source_scores
+            }
+            for item in combination_candidates
+        ],
+        "sug_count": len(all_sugs),
+        "sug_details": sug_details,
+        "high_score_sug_count": len(high_score_sugs),
+        "high_gain_sugs": [{"text": q.text, "score": q.score_with_o, "type": "sug"} for q in q_list_next if q.from_source == "sug"],
+        "search_count": len(search_list),
+        "search_results": search_results_data,
+        "q_list_next_size": len(q_list_next),
+        "q_list_next_sections": {
+            "sugs": [
+                {
+                    "text": item[0].text,
+                    "score": item[0].score_with_o,
+                    "from_source": "sug"
+                }
+                for item in sug_candidates
+            ],
+            "domain_combinations": [
+                {
+                    "text": item[0].text,
+                    "score": item[0].score_with_o,
+                    "from_source": "domain_comb",
+                    "is_above_source_scores": item[1].is_above_source_scores
+                }
+                for item in combination_candidates
+            ]
+        }
+    })
+    context.rounds.append(round_data)
+
+    print(f"\nRound {round_num} 总结:")
+    print(f"  输入Query数: {len(query_input)}")
+    print(f"  域组合数: {len(domain_combinations)}")
+    print(f"  高分组合: {len(high_score_combinations)}")
+    print(f"  SUG数: {len(all_sugs)}")
+    print(f"  高分SUG数: {len(high_score_sugs)}")
+    print(f"  高增益SUG: {len(high_gain_sugs)}")
+    print(f"  搜索数: {len(search_list)}")
+    print(f"  下轮Query数: {len(q_list_next)}")
+
+    return q_list_next, search_list
+
+
+async def iterative_loop_v2(
+    context: RunContext,
+    max_rounds: int = 4,
+    sug_threshold: float = 0.7
+):
+    """v121 主迭代循环"""
+
+    print(f"\n{'='*60}")
+    print(f"开始v121迭代循环(语义分段跨域组词版)")
+    print(f"最大轮数: {max_rounds}")
+    print(f"sug阈值: {sug_threshold}")
+    print(f"{'='*60}")
+
+    # Round 0: 初始化(语义分段 + 拆词)
+    segments = await initialize_v2(context.o, context)
+
+    # API实例
+    xiaohongshu_api = XiaohongshuSearchRecommendations()
+    xiaohongshu_search = XiaohongshuSearch()
+
+    # 收集所有搜索结果
+    all_search_list = []
+
+    # 跨轮次搜索去重集合
+    searched_texts = set()
+
+    # 准备 Round 1 的输入:从 segments 提取所有 words
+    query_input = extract_words_from_segments(segments)
+    print(f"\n提取了 {len(query_input)} 个词作为 Round 1 的输入")
+
+    # Round 1-N: 迭代循环
+    num_segments = len(segments)
+    actual_max_rounds = min(max_rounds, num_segments)
+    round_num = 1
+
+    while query_input and round_num <= actual_max_rounds:
+        query_input, search_list = await run_round_v2(
+            round_num=round_num,
+            query_input=query_input,  # 传递上一轮的输出
+            segments=segments,
+            o=context.o,
+            context=context,
+            xiaohongshu_api=xiaohongshu_api,
+            xiaohongshu_search=xiaohongshu_search,
+            sug_threshold=sug_threshold,
+            searched_texts=searched_texts  # 传递已搜索词集合
+        )
+
+        all_search_list.extend(search_list)
+
+        # 如果没有新的query,提前结束
+        if not query_input:
+            print(f"\n第{round_num}轮后无新query生成,提前结束迭代")
+            break
+
+        round_num += 1
+
+    print(f"\n{'='*60}")
+    print(f"迭代完成")
+    print(f"  实际轮数: {round_num}")
+    print(f"  总搜索次数: {len(all_search_list)}")
+    print(f"  总帖子数: {sum(len(s.post_list) for s in all_search_list)}")
+    print(f"{'='*60}")
+
+    return all_search_list
+
+
+# ============================================================================
+# 主函数
+# ============================================================================
+
+async def main(input_dir: str, max_rounds: int = 2, sug_threshold: float = 0.7, visualize: bool = False):
+    """主函数"""
+    current_time, log_url = set_trace()
+
+    # 读取输入
+    input_context_file = os.path.join(input_dir, 'context.md')
+    input_q_file = os.path.join(input_dir, 'q.md')
+
+    c = read_file_as_string(input_context_file)  # 原始需求
+    o = read_file_as_string(input_q_file)  # 原始问题
+
+    # 版本信息
+    version = os.path.basename(__file__)
+    version_name = os.path.splitext(version)[0]
+
+    # 日志目录
+    log_dir = os.path.join(input_dir, "output", version_name, current_time)
+
+    # 创建运行上下文
+    run_context = RunContext(
+        version=version,
+        input_files={
+            "input_dir": input_dir,
+            "context_file": input_context_file,
+            "q_file": input_q_file,
+        },
+        c=c,
+        o=o,
+        log_dir=log_dir,
+        log_url=log_url,
+    )
+
+    # 创建日志目录
+    os.makedirs(run_context.log_dir, exist_ok=True)
+
+    # 配置日志文件
+    log_file_path = os.path.join(run_context.log_dir, "run.log")
+    log_file = open(log_file_path, 'w', encoding='utf-8')
+
+    # 重定向stdout到TeeLogger(同时输出到控制台和文件)
+    original_stdout = sys.stdout
+    sys.stdout = TeeLogger(original_stdout, log_file)
+
+    try:
+        print(f"📝 日志文件: {log_file_path}")
+        print(f"{'='*60}\n")
+
+        # 执行迭代 (v121: 使用新架构)
+        all_search_list = await iterative_loop_v2(
+            run_context,
+            max_rounds=max_rounds,
+            sug_threshold=sug_threshold
+        )
+
+        # 格式化输出
+        output = f"原始需求:{run_context.c}\n"
+        output += f"原始问题:{run_context.o}\n"
+        output += f"总搜索次数:{len(all_search_list)}\n"
+        output += f"总帖子数:{sum(len(s.post_list) for s in all_search_list)}\n"
+        output += "\n" + "="*60 + "\n"
+
+        if all_search_list:
+            output += "【搜索结果】\n\n"
+            for idx, search in enumerate(all_search_list, 1):
+                output += f"{idx}. 搜索词: {search.text} (分数: {search.score_with_o:.2f})\n"
+                output += f"   帖子数: {len(search.post_list)}\n"
+                if search.post_list:
+                    for post_idx, post in enumerate(search.post_list[:3], 1):  # 只显示前3个
+                        output += f"   {post_idx}) {post.title}\n"
+                        output += f"      URL: {post.note_url}\n"
+                output += "\n"
+        else:
+            output += "未找到搜索结果\n"
+
+        run_context.final_output = output
+
+        print(f"\n{'='*60}")
+        print("最终结果")
+        print(f"{'='*60}")
+        print(output)
+
+        # 保存上下文文件
+        context_file_path = os.path.join(run_context.log_dir, "run_context.json")
+        context_dict = run_context.model_dump()
+        with open(context_file_path, "w", encoding="utf-8") as f:
+            json.dump(context_dict, f, ensure_ascii=False, indent=2)
+        print(f"\nRunContext saved to: {context_file_path}")
+
+        # 保存详细的搜索结果
+        search_results_path = os.path.join(run_context.log_dir, "search_results.json")
+        search_results_data = [s.model_dump() for s in all_search_list]
+        with open(search_results_path, "w", encoding="utf-8") as f:
+            json.dump(search_results_data, f, ensure_ascii=False, indent=2)
+        print(f"Search results saved to: {search_results_path}")
+
+        # 可视化
+        if visualize:
+            import subprocess
+            output_html = os.path.join(run_context.log_dir, "visualization.html")
+            print(f"\n🎨 生成可视化HTML...")
+
+            # 获取绝对路径
+            abs_context_file = os.path.abspath(context_file_path)
+            abs_output_html = os.path.abspath(output_html)
+
+            # 运行可视化脚本
+            result = subprocess.run([
+                "node",
+                "visualization/sug_v6_1_2_121/index.js",
+                abs_context_file,
+                abs_output_html
+            ])
+
+            if result.returncode == 0:
+                print(f"✅ 可视化已生成: {output_html}")
+            else:
+                print(f"❌ 可视化生成失败")
+
+    finally:
+        # 恢复stdout
+        sys.stdout = original_stdout
+        log_file.close()
+        print(f"\n📝 运行日志已保存: {log_file_path}")
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description="搜索query优化工具 - v6.1.2.121 语义分段跨域组词版")
+    parser.add_argument(
+        "--input-dir",
+        type=str,
+        default="input/旅游-逸趣玩旅行/如何获取能体现川西秋季特色的高质量风光摄影素材?",
+        help="输入目录路径,默认: input/旅游-逸趣玩旅行/如何获取能体现川西秋季特色的高质量风光摄影素材?"
+    )
+    parser.add_argument(
+        "--max-rounds",
+        type=int,
+        default=4,
+        help="最大轮数,默认: 4"
+    )
+    parser.add_argument(
+        "--sug-threshold",
+        type=float,
+        default=0.7,
+        help="suggestion阈值,默认: 0.7"
+    )
+    parser.add_argument(
+        "--visualize",
+        action="store_true",
+        default=True,
+        help="运行完成后自动生成可视化HTML"
+    )
+    args = parser.parse_args()
+
+    asyncio.run(main(args.input_dir, max_rounds=args.max_rounds, sug_threshold=args.sug_threshold, visualize=args.visualize))

+ 3941 - 0
sug_v6_1_2_127.py

@@ -0,0 +1,3941 @@
+import asyncio
+import json
+import os
+import sys
+import argparse
+from datetime import datetime
+from typing import Literal, Optional
+
+from agents import Agent, Runner, ModelSettings
+from lib.my_trace import set_trace
+from pydantic import BaseModel, Field
+
+from lib.utils import read_file_as_string
+from lib.client import get_model
+MODEL_NAME = "google/gemini-2.5-flash"
+# 得分提升阈值:sug或组合词必须比来源query提升至少此幅度才能进入下一轮
+REQUIRED_SCORE_GAIN = 0.02
+from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
+from script.search.xiaohongshu_search import XiaohongshuSearch
+
+
+# ============================================================================
+# 日志工具类
+# ============================================================================
+
+class TeeLogger:
+    """同时输出到控制台和日志文件的工具类"""
+    def __init__(self, stdout, log_file):
+        self.stdout = stdout
+        self.log_file = log_file
+
+    def write(self, message):
+        self.stdout.write(message)
+        self.log_file.write(message)
+        self.log_file.flush()  # 实时写入,避免丢失日志
+
+    def flush(self):
+        self.stdout.flush()
+        self.log_file.flush()
+
+
+# ============================================================================
+# 数据模型
+# ============================================================================
+
+class Seg(BaseModel):
+    """分词(旧版)- v120使用"""
+    text: str
+    score_with_o: float = 0.0  # 与原始问题的评分
+    reason: str = ""  # 评分理由
+    from_o: str = ""  # 原始问题
+
+
+# ============================================================================
+# 新架构数据模型 (v121)
+# ============================================================================
+
+class Segment(BaseModel):
+    """语义片段(Round 0语义分段结果)"""
+    text: str  # 片段文本
+    type: str  # 语义类型: 疑问标记/核心动作/修饰短语/中心名词/逻辑连接
+    score_with_o: float = 0.0  # 与原始问题的评分
+    reason: str = ""  # 评分理由
+    from_o: str = ""  # 原始问题
+    words: list[str] = Field(default_factory=list)  # 该片段拆分出的词列表(Round 0拆词结果)
+    word_scores: dict[str, float] = Field(default_factory=dict)  # 词的评分 {word: score}
+    word_reasons: dict[str, str] = Field(default_factory=dict)  # 词的评分理由 {word: reason}
+
+
+class DomainCombination(BaseModel):
+    """域组合(Round N的N域组合结果)"""
+    text: str  # 组合后的文本
+    domains: list[int] = Field(default_factory=list)  # 参与组合的域索引列表(对应segments的索引)
+    type_label: str = ""  # 类型标签,如 [疑问标记+核心动作+中心名词]
+    source_words: list[list[str]] = Field(default_factory=list)  # 来源词列表,每个元素是一个域的词列表,如 [["猫咪"], ["梗图"]]
+    score_with_o: float = 0.0  # 与原始问题的评分
+    reason: str = ""  # 评分理由
+    from_segments: list[str] = Field(default_factory=list)  # 来源segment的文本列表
+    source_word_details: list[dict] = Field(default_factory=list)  # 词及其得分信息 [{"domain_index":0,"segment_type":"","words":[{"text":"","score":0.0}]}]
+    source_scores: list[float] = Field(default_factory=list)  # 来源词的分数列表(扁平化)
+    max_source_score: float | None = None  # 来源词的最高分
+    is_above_source_scores: bool = False  # 组合得分是否超过所有来源词
+
+
+# ============================================================================
+# 旧架构数据模型(保留但不使用)
+# ============================================================================
+
+# class Word(BaseModel):
+#     """词(旧版)- v120使用,v121不再使用"""
+#     text: str
+#     score_with_o: float = 0.0  # 与原始问题的评分
+#     from_o: str = ""  # 原始问题
+
+
+class Word(BaseModel):
+    """词"""
+    text: str
+    score_with_o: float = 0.0  # 与原始问题的评分
+    from_o: str = ""  # 原始问题
+
+
+class QFromQ(BaseModel):
+    """Q来源信息(用于Sug中记录)"""
+    text: str
+    score_with_o: float = 0.0
+
+
+class Q(BaseModel):
+    """查询"""
+    text: str
+    score_with_o: float = 0.0  # 与原始问题的评分
+    reason: str = ""  # 评分理由
+    from_source: str = ""  # v120: seg/sug/add; v121新增: segment/domain_comb/sug
+    type_label: str = ""  # v121新增:域类型标签(仅用于domain_comb来源)
+    domain_index: int = -1  # v121新增:域索引(word来源时有效,-1表示无域)
+    domain_type: str = ""  # v121新增:域类型(word来源时表示所属segment的type,如"中心名词")
+
+
+class Sug(BaseModel):
+    """建议词"""
+    text: str
+    score_with_o: float = 0.0  # 与原始问题的评分
+    reason: str = ""  # 评分理由
+    from_q: QFromQ | None = None  # 来自的q
+
+
+class Seed(BaseModel):
+    """种子(旧版)- v120使用,v121不再使用"""
+    text: str
+    added_words: list[str] = Field(default_factory=list)  # 已经增加的words
+    from_type: str = ""  # seg/sug/add
+    score_with_o: float = 0.0  # 与原始问题的评分
+
+
+class Post(BaseModel):
+    """帖子"""
+    title: str = ""
+    body_text: str = ""
+    type: str = "normal"  # video/normal
+    images: list[str] = Field(default_factory=list)  # 图片url列表,第一张为封面
+    video: str = ""  # 视频url
+    interact_info: dict = Field(default_factory=dict)  # 互动信息
+    note_id: str = ""
+    note_url: str = ""
+
+
+class Search(Sug):
+    """搜索结果(继承Sug)"""
+    post_list: list[Post] = Field(default_factory=list)  # 搜索得到的帖子列表
+
+
+class RunContext(BaseModel):
+    """运行上下文"""
+    version: str
+    input_files: dict[str, str]
+    c: str  # 原始需求
+    o: str  # 原始问题
+    log_url: str
+    log_dir: str
+
+    # v121新增:语义分段结果
+    segments: list[dict] = Field(default_factory=list)  # Round 0的语义分段结果
+
+    # 每轮的数据
+    rounds: list[dict] = Field(default_factory=list)  # 每轮的详细数据
+
+    # 最终结果
+    final_output: str | None = None
+
+    # 评估缓存:避免重复评估相同文本
+    evaluation_cache: dict[str, tuple[float, str]] = Field(default_factory=dict)
+    # key: 文本, value: (score, reason)
+
+    # 历史词/组合得分追踪(用于Round 2+计算系数)
+    word_score_history: dict[str, float] = Field(default_factory=dict)
+    # key: 词/组合文本, value: 最终得分
+
+
+# ============================================================================
+# Agent 定义
+# ============================================================================
+
+# ============================================================================
+# v121 新增 Agent
+# ============================================================================
+
+# Agent: 语义分段专家 (Prompt1)
+class SemanticSegment(BaseModel):
+    """单个语义片段"""
+    segment_text: str = Field(..., description="片段文本")
+    segment_type: str = Field(..., description="语义类型(疑问标记/核心动作/修饰短语/中心名词/逻辑连接)")
+    reasoning: str = Field(..., description="分段理由")
+
+
+class SemanticSegmentation(BaseModel):
+    """语义分段结果"""
+    segments: list[SemanticSegment] = Field(..., description="语义片段列表")
+    overall_reasoning: str = Field(..., description="整体分段思路")
+
+
+semantic_segmentation_instructions = """
+你是语义分段专家。给定一个搜索query,将其拆分成不同语义类型的片段。
+
+## 语义类型定义
+1. **疑问引导**:如何、怎么、什么、哪里等疑问词
+2. **核心动作**:关键动词,如获取、制作、拍摄、寻找等
+3. **修饰短语**:形容词、副词等修饰成分
+4. **中心名词**:核心名词
+5. **逻辑连接**:并且、或者、以及等连接词(较少出现)
+
+## 分段原则
+1. **语义完整性**:每个片段应该是一个完整的语义单元
+2. **类型互斥**:每个片段只能属于一种类型
+3. **保留原文**:片段文本必须保留原query中的字符,不得改写
+4. **顺序保持**:片段顺序应与原query一致
+
+
+## 输出要求
+- segments: 片段列表
+  - segment_text: 片段文本(必须来自原query)
+  - segment_type: 语义类型(从5种类型中选择)
+  - reasoning: 为什么这样分段
+- overall_reasoning: 整体分段思路
+
+## JSON输出规范
+1. **格式要求**:必须输出标准JSON格式
+2. **引号规范**:字符串中如需表达引用,使用书名号《》或「」,不要使用英文引号或中文引号""
+""".strip()
+
+semantic_segmenter = Agent[None](
+    name="语义分段专家",
+    instructions=semantic_segmentation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=SemanticSegmentation,
+)
+
+
+# ============================================================================
+# v120 保留 Agent
+# ============================================================================
+
+# Agent 1: 分词专家(v121用于Round 0拆词)
+class WordSegmentation(BaseModel):
+    """分词结果"""
+    words: list[str] = Field(..., description="分词结果列表")
+    reasoning: str = Field(..., description="分词理由")
+
+word_segmentation_instructions = """
+你是分词专家。给定一个query,将其拆分成有意义的最小单元。
+
+## 分词原则
+1. 保留有搜索意义的词汇
+2. 拆分成独立的概念
+3. 保留专业术语的完整性
+4. 去除虚词(的、吗、呢等),但保留疑问词(如何、为什么、怎样等)
+
+## 输出要求
+返回分词列表和分词理由。
+""".strip()
+
+word_segmenter = Agent[None](
+    name="分词专家",
+    instructions=word_segmentation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=WordSegmentation,
+)
+
+
+# Agent 2: 动机维度评估专家 + 品类维度评估专家(两阶段评估)
+
+# 动机评估的嵌套模型
+class CoreMotivationExtraction(BaseModel):
+    """核心动机提取"""
+    简要说明核心动机: str = Field(..., description="核心动机说明")
+
+class MotivationEvaluation(BaseModel):
+    """动机维度评估"""
+    原始问题核心动机提取: CoreMotivationExtraction = Field(..., description="原始问题核心动机提取")
+    动机维度得分: float = Field(..., description="动机维度得分 -1~1")
+    简要说明动机维度相关度理由: str = Field(..., description="动机维度相关度理由")
+    得分为零的原因: Optional[Literal["原始问题无动机", "sug词条无动机", "动机不匹配", "不适用"]] = Field(None, description="当得分为0时的原因分类(可选,仅SUG评估使用)")
+
+class CategoryEvaluation(BaseModel):
+    """品类维度评估"""
+    品类维度得分: float = Field(..., description="品类维度得分 -1~1")
+    简要说明品类维度相关度理由: str = Field(..., description="品类维度相关度理由")
+
+class ExtensionWordEvaluation(BaseModel):
+    """延伸词评估"""
+    延伸词得分: float = Field(..., ge=-1, le=1, description="延伸词得分 -1~1")
+    简要说明延伸词维度相关度理由: str = Field(..., description="延伸词维度相关度理由")
+
+# 动机评估 prompt(统一版本)
+motivation_evaluation_instructions = """
+# 角色
+你是**专业的动机意图评估专家**。
+任务:判断<平台sug词条>与<原始问题>的**动机意图匹配度**,给出**-1到1之间**的数值评分。
+
+---
+# 输入信息
+你将接收到以下输入:
+- **<原始问题>**:用户的初始查询问题,代表用户的真实需求意图。
+- **<平台sug词条>**:待评估的词条,可能是单个或多个作用域的组合
+---
+
+
+# 核心约束
+
+## 维度独立性声明
+【严格约束】本评估**仅评估动机意图维度**:
+- **只评估** 用户"想要做什么",即原始问题的行为意图和目的
+- 核心是 **动词**:获取、学习、拍摄、制作、寻找等
+- 包括:核心动作 + 使用场景 + 最终目的
+- **评估重点**:动作本身及其语义方向
+ **禁止使用"主题相关"作为评分依据**:评分理由中不得出现"主题"、"内容"、"话题"等词
+
+---
+
+# 作用域与动作意图
+
+## 什么是作用域?
+**作用域 = 动机层 + 对象层 + 场景层**
+
+## 动作意图的识别
+
+### 方法1: 显性动词直接提取
+
+当原始问题明确包含动词时,直接提取
+示例:
+"如何获取素材" → 核心动机 = "获取"
+"寻找拍摄技巧" → 核心动机 = "寻找"(或"学习")
+"制作视频教程" → 核心动机 = "制作"
+
+### 方法2: 隐性动词语义推理
+当原始问题没有显性动词时,需要结合上下文推理
+
+如果原始问题是纯名词短语,无任何动作线索:
+→ 核心动机 = 无法识别
+→ 在此情况下,动机维度得分应为 0。
+示例:
+"摄影" → 无法识别动机,动机维度得分 = 0
+"川西风光" → 无法识别动机,动机维度得分 = 0
+
+---
+
+# 部分作用域的处理
+
+## 情况1:sug词条是原始问题的部分作用域
+
+当sug词条只包含原始问题的部分作用域时,需要判断:
+1. sug词条是否包含动作意图
+2. 如果包含,动作是否匹配
+
+**示例**:
+```
+原始问题:"川西旅行行程规划"
+- 完整作用域:规划(动作)+ 旅行行程(对象)+ 川西(场景)
+
+Sug词条:"川西旅行"
+- 包含作用域:旅行(部分对象)+ 川西(场景)
+- 缺失作用域:规划(动作)
+- 动作意图评分:0(无动作意图)
+```
+
+**评分原则**:
+- 如果sug词条缺失动机层(动作) → 动作意图得分 = 0
+- 如果sug词条包含动机层 → 按动作匹配度评分
+
+---
+
+# 评分标准
+
+## 【正向匹配】
+
+### +0.9~1.0:核心动作完全一致
+**示例**:
+- "规划旅行行程" vs "安排旅行路线" → 0.98
+  - 规划≈安排,语义完全一致
+- "获取素材" vs "下载素材" → 0.97
+  - 获取≈下载,语义完全一致
+
+- 特殊规则: 如果sug词的核心动作是原始问题动作的**具体化子集**,也判定为完全一致
+例: 原始问题"扣除猫咪主体的方法" vs sug词"扣除猫咪眼睛的方法"(子集但目的一致
+**注意**:此处不考虑对象和场景是否一致,只看动作本身
+
+###+0.75~0.95: 核心动作语义相近或为同义表达
+  - 例: 原始问题"如何获取素材" vs sug词"如何下载素材"
+  - 同义词对: 获取≈下载≈寻找, 技巧≈方法≈教程≈攻略
+
+### +0.50~0.75:动作意图相关
+**判定标准**:
+- 动作是实现原始意图的相关路径
+- 或动作是原始意图的前置/后置步骤
+
+**示例**:
+- "获取素材" vs "管理素材" → 0.65
+  - 管理是获取后的相关步骤
+- "规划行程" vs "预订酒店" → 0.60
+  - 预订是规划的具体实施步骤
+
+### +0.25~0.50:动作意图弱相关
+**判定标准**:
+- 动作在同一大类但方向不同
+- 或动作有间接关联
+
+**示例**:
+- "学习摄影技巧" vs "欣赏摄影作品" → 0.35
+  - 都与摄影有关,但学习≠欣赏
+- "规划旅行" vs "回忆旅行" → 0.30
+  - 都与旅行有关,但方向不同
+
+---
+
+## 【中性/无关】
+
+### 0:无动作意图或动作完全无关
+**适用场景**:
+1. 原始问题或sug词条无法识别动作
+2. 两者动作意图完全无关
+
+**示例**:
+- "如何获取素材" vs "摄影器材" → 0
+  - sug词条无动作意图
+- "川西风光" vs "风光摄影作品" → 0
+  - 原始问题无动作意图
+
+**理由模板**:
+- "sug词条无明确动作意图,无法评估动作匹配度"
+- "原始问题无明确动作意图,动作维度得分为0"
+
+---
+
+## 【负向偏离】
+
+### -0.2~-0.05:动作方向轻度偏离
+**示例**:
+- "学习摄影技巧" vs "销售摄影课程" → -0.10
+  - 学习 vs 销售,方向有偏差
+
+### -0.5~-0.25:动作意图明显冲突
+**示例**:
+- "获取免费素材" vs "购买素材" → -0.35
+  - 获取免费 vs 购买,明显冲突
+
+### -1.0~-0.55:动作意图完全相反
+**示例**:
+- "下载素材" vs "上传素材" → -0.70
+  - 下载 vs 上传,方向完全相反
+
+---
+
+## 得分为零的原因(语义判断)
+
+当动机维度得分为 0 时,需要在 `得分为零的原因` 字段中选择以下之一:
+- **"原始问题无动机"**:原始问题是纯名词短语,无法识别任何动作意图
+- **"sug词条无动机"**:sug词条中不包含任何动作意图
+- **"动机不匹配"**:双方都有动作,但完全无关联
+- **"不适用"**:得分不为零时使用此默认值
+
+---
+
+# 输出格式
+输出结果必须为一个 **JSON 格式**,包含以下内容:
+```json
+{
+  "原始问题核心动机提取": {
+    "简要说明核心动机": ""
+  },
+  "动机维度得分": "-1到1之间的小数",
+  "简要说明动机维度相关度理由": "评估该sug词条与原始问题动机匹配程度的理由,包含作用域覆盖情况",
+  "得分为零的原因": "原始问题无动机/sug词条无动机/动机不匹配/不适用"
+}
+```
+
+**输出约束(非常重要)**:
+1. **字符串长度限制**:\"简要说明动机维度相关度理由\"字段必须控制在**150字以内**
+2. **JSON格式规范**:必须生成完整的JSON格式,确保字符串用双引号包裹且正确闭合
+3. **引号使用**:字符串中如需表达引用,请使用《》或「」代替单引号或双引号
+
+---
+
+# 核心原则总结
+1. **只评估动作**:完全聚焦于动作意图,不管对象和场景
+2. **作用域识别**:识别作用域但只评估动机层
+3. **严格标准一致性**:对所有用例使用相同的评估标准,避免评分飘移
+4. **理由纯粹**:评分理由只能谈动作,不能谈对象、场景、主题
+""".strip()
+
+# 品类评估 prompt
+category_evaluation_instructions = """
+# 角色
+你是**专业的内容主体评估专家**。
+任务:判断<平台sug词条>与<原始问题>的**内容主体匹配度**,给出**-1到1之间**的数值评分。
+
+---
+
+# 输入信息
+- **<原始问题>**:用户的完整需求描述
+- **<平台sug词条>**:待评估的词条,可能是单个或多个作用域的组合
+---
+
+
+# 核心约束
+
+## 维度独立性声明
+【严格约束】本评估**仅评估内容主体维度**:
+- **只评估**:名词主体 + 限定词(地域、时间、场景、质量等)
+- **完全忽略**:动作、意图、目的
+- **评估重点**:内容本身的主题和属性
+
+---
+
+# 作用域与内容主体
+
+## 什么是作用域?
+**作用域 = 动机层 + 对象层 + 场景层**
+
+在Prompt2中:
+- **动机层(动作)完全忽略**
+- **只评估对象层 + 场景层(限定词)**
+
+## 内容主体的构成
+
+**内容主体 = 核心名词 + 限定词**
+
+
+---
+
+# 作用域覆盖度评估
+
+## 核心原则:越完整越高分
+
+**完整性公式**:
+```
+作用域覆盖度 = sug词条包含的作用域元素 / 原始问题的作用域元素总数
+```
+
+**评分影响**:
+- 覆盖度100% → 基础高分(0.9+)
+- 覆盖度50-99% → 中高分(0.6-0.9)
+- 覆盖度<50% → 中低分(0.3-0.6)
+- 覆盖度=0 → 低分或0分
+
+---
+
+## 部分作用域的处理
+
+### 情况1:sug词条包含原始问题的所有对象层和场景层元素
+**评分**:0.95-1.0
+
+**示例**:
+```
+原始问题:"川西秋季风光摄影素材"
+- 对象层:摄影素材
+- 场景层:川西 + 秋季 + 风光
+
+Sug词条:"川西秋季风光摄影作品"
+- 对象层:摄影作品(≈素材)
+- 场景层:川西 + 秋季 + 风光
+- 覆盖度:100%
+- 评分:0.98
+```
+
+### 情况2:sug词条包含部分场景层元素
+**评分**:根据覆盖比例
+
+**示例**:
+```
+原始问题:"川西秋季风光摄影素材"
+- 对象层:摄影素材
+- 场景层:川西 + 秋季 + 风光(3个元素)
+
+Sug词条:"川西风光摄影素材"
+- 对象层:摄影素材 ✓
+- 场景层:川西 + 风光(2个元素)
+- 覆盖度:(1+2)/(1+3) = 75%
+- 评分:0.85
+```
+
+### 情况3:sug词条只包含对象层,无场景层
+**评分**:根据对象匹配度和覆盖度
+
+**示例**:
+```
+原始问题:"川西秋季风光摄影素材"
+- 对象层:摄影素材
+- 场景层:川西 + 秋季 + 风光
+
+Sug词条:"摄影素材"
+- 对象层:摄影素材 ✓
+- 场景层:无
+- 覆盖度:1/4 = 25%
+- 评分:0.50(对象匹配但缺失所有限定)
+```
+
+### 情况4:sug词条只包含场景层,无对象层
+**评分**:较低分
+
+**示例**:
+```
+原始问题:"川西旅行行程规划"
+- 对象层:旅行行程
+- 场景层:川西
+
+Sug词条:"川西"
+- 对象层:无
+- 场景层:川西 ✓
+- 覆盖度:1/2 = 50%
+- 评分:0.35(只有场景,缺失核心对象)
+```
+
+---
+
+# 评估核心原则
+
+## 原则1:只看表面词汇,禁止联想推演
+**严格约束**:只能基于sug词实际包含的词汇评分
+
+**错误案例**:
+- ❌ "川西旅行" vs "旅行"
+  - 错误:"旅行可以包括川西,所以有关联" → 评分0.7
+  - 正确:"sug词只有'旅行',无'川西',缺失地域限定" → 评分0.50
+
+
+---
+
+# 评分标准
+
+## 【正向匹配】
+
++0.95~1.0: 核心主体+所有关键限定词完全匹配
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"川西秋季风光摄影作品"
+
++0.75~0.95: 核心主体匹配,存在限定词匹配
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"川西风光摄影素材"(缺失"秋季")
+
++0.5~0.75: 核心主体匹配,无限定词匹配或合理泛化
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"四川风光摄影"
+
++0.3~0.5: 核心主体匹配,但限定词缺失或存在语义错位
+  - 特别注意"语义身份"差异,主体词出现但上下文语义不同
+  - 例:
+    · "猫咪的XX行为"(猫咪是行为者)
+    · vs "用猫咪表达XX的梗图"(猫咪是媒介)
+    · 虽都含"猫咪+XX",但语义角色不同
+
++0.2~0.3: 主体词不匹配,限定词缺失或错位
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"风光摄影入门"
+
++0.05~0.2: 主体词过度泛化或仅抽象相似
+  - 例: sug词是通用概念,原始问题是特定概念
+    sug词"每日计划"(通用)vs 原始问题 "川西旅行行程"(特定)
+      → 评分:0.08
+
+【中性/无关】
+0: 类别明显不同,没有明确目的,无明确关联
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"人像摄影素材"
+  - 例: 原始问题无法识别动机 且 sug词也无明确动作 → 0
+
+【负向偏离】
+-0.2~-0.05: 主体词或限定词存在误导性
+  - 例: 原始问题"免费摄影素材" vs sug词"付费摄影素材库"
+
+-0.5~-0.25: 主体词明显错位或品类冲突
+  - 例: 原始问题"风光摄影素材" vs sug词"人像修图教程"
+
+-1.0~-0.55: 完全错误的品类或有害引导
+  - 例: 原始问题"正版素材获取" vs sug词"盗版素材下载"
+
+
+---
+
+# 输出格式
+输出结果必须为一个 **JSON 格式**,包含以下内容:
+```json
+{
+  "品类维度得分": "-1到1之间的小数",
+  "简要说明品类维度相关度理由": "评估该sug词条与原始问题品类匹配程度的理由,包含作用域覆盖理由"
+}
+```
+
+**输出约束(非常重要)**:
+1. **字符串长度限制**:\"简要说明品类维度相关度理由\"字段必须控制在**150字以内**
+2. **JSON格式规范**:必须生成完整的JSON格式,确保字符串用双引号包裹且正确闭合
+3. **引号使用**:字符串中如需表达引用,请使用《》或「」代替单引号或双引号
+
+---
+
+# 核心原则总结
+
+1. **只看名词和限定词**:完全忽略动作和意图
+2. **作用域覆盖优先**:覆盖的作用域元素越多,分数越高
+3. **禁止联想推演**:只看sug词实际包含的词汇
+4. **通用≠特定**:通用概念不等于特定概念
+5. **理由纯粹**:评分理由只能谈对象、限定词、覆盖度
+""".strip()
+
+# 延伸词评估 prompt
+extension_word_evaluation_instructions = """
+# 角色
+你是**专业的延伸词语义评估专家**。
+任务:识别<平台sug词条>中的延伸词,评估其对原始问题作用域的补全度和目的贡献度,给出**-1到1之间**的数值评分。
+
+---
+# 输入信息
+- **<原始问题>**:用户的完整需求描述
+- **<平台sug词条>**:待评估的词条,可能是单个或多个作用域的组合
+---
+
+# 核心概念
+
+## 什么是延伸词?
+**延伸词**:<平台sug词条>中出现,但不属于<原始问题>作用域范围内的词汇或概念
+
+**关键判断**:
+```
+IF sug词的词汇属于原始问题的作用域元素(动机/对象/场景):
+   → 不是延伸词,是作用域内的词
+
+IF sug词的词汇不属于原始问题的作用域:
+   → 是延伸词
+   → 由Prompt3评估
+```
+
+---
+
+# 作用域与延伸词
+
+## 作用域
+**作用域 = 动机层 + 对象层 + 场景层**
+
+**非延伸词示例**(属于作用域内):
+```
+原始问题:"川西旅行行程规划"
+作用域:
+- 动机层:规划
+- 对象层:旅行行程
+- 场景层:川西
+
+Sug词条:"川西旅行行程规划攻略"
+- "川西"→ 属于场景层,不是延伸词
+- "旅行"→ 属于对象层,不是延伸词
+- "行程"→ 属于对象层,不是延伸词
+- "规划"→ 属于动机层,不是延伸词
+- "攻略"→ 与"规划"同义,不是延伸词
+- 结论:无延伸词
+```
+
+**延伸词示例**(不属于作用域):
+```
+原始问题:"川西旅行行程规划"
+作用域:规划 + 旅行行程 + 川西
+
+Sug词条:"川西旅行行程规划住宿推荐"
+- "住宿推荐"→ 不属于原始问题任何作用域
+- 结论:延伸词 = ["住宿推荐"]
+```
+
+---
+
+# 延伸词识别方法
+
+## 步骤1:提取原始问题的作用域元素
+```
+动机层:提取动作及其同义词
+对象层:提取核心名词及其同义词
+场景层:提取所有限定词
+```
+
+## 步骤2:提取sug词条的所有关键词
+```
+提取sug词条中的所有实词(名词、动词、形容词)
+```
+
+## 步骤3:匹配判定
+```
+FOR 每个sug词条关键词:
+   IF 该词 ∈ 原始问题作用域元素(包括同义词):
+      → 不是延伸词
+   ELSE:
+      → 是延伸词
+```
+
+## 步骤4:同义词/相近词判定规则
+
+### 不算延伸词的情况:
+**同义词**:
+- 行程 ≈ 路线 ≈ 安排 ≈ 计划
+- 获取 ≈ 下载 ≈ 寻找 ≈ 收集
+- 技巧 ≈ 方法 ≈ 教程 ≈ 攻略
+- 素材 ≈ 资源 ≈ 作品 ≈ 内容
+
+**具体化/细化**:
+- 原始:"川西旅游" + sug词:"稻城亚丁"(川西的具体地点)→ 不算延伸
+- 原始:"摄影技巧" + sug词:"风光摄影"(摄影的细化)→ 不算延伸
+- 原始:"素材" + sug词:"高清素材"(素材的质量细化)→ 不算延伸
+
+**判定逻辑**:
+```
+IF sug词的概念是原始问题概念的子集/下位词/同义词:
+   → 不算延伸词
+   → 视为对原问题的细化或重述
+```
+
+---
+
+### 算延伸词的情况:
+
+**新增维度**:原始问题未涉及的信息维度
+- 原始:"川西旅行" + sug词:"住宿" → 延伸词
+- 原始:"摄影素材" + sug词:"版权" → 延伸词
+
+**新增限定条件**:原始问题未提及的约束
+- 原始:"素材获取" + sug词:"免费" → 延伸词
+- 原始:"旅行行程" + sug词:"7天" → 延伸词
+
+**扩展主题**:相关但非原问题范围
+- 原始:"川西行程" + sug词:"美食推荐" → 延伸词
+- 原始:"摄影技巧" + sug词:"后期修图" → 延伸词
+
+**工具/方法**:原始问题未提及的具体工具
+- 原始:"视频剪辑" + sug词:"PR软件" → 延伸词
+- 原始:"图片处理" + sug词:"PS教程" → 延伸词
+
+---
+
+# 延伸词类型与评分
+
+## 核心评估维度:对原始问题作用域的贡献
+
+### 维度1:作用域补全度
+延伸词是否帮助sug词条更接近原始问题的完整作用域?
+
+
+### 维度2:目的达成度
+延伸词是否促进原始问题核心目的的达成?
+---
+####类型1:作用域增强型
+**定义**:延伸词是原始问题核心目的,或补全关键作用域
+**得分范围**:+0.12~+0.20
+
+**判定标准**:
+- 使sug词条更接近原始问题的完整需求
+---
+
+####类型2:作用域辅助型
+**定义**:延伸词对核心目的有辅助作用,但非必需
+
+**得分范围**:+0.05~+0.12
+
+**判定标准**:
+- sug词条更丰富但不改变原始需求核心
+
+---
+
+####类型3:作用域无关型
+**定义**:延伸词与核心目的无实质关联
+
+**得分**:0
+
+**示例**:
+- 原始:"如何拍摄风光" + 延伸词:"相机品牌排行"
+  - 评分:0
+  - 理由:品牌排行与拍摄技巧无关
+
+---
+
+####类型4:作用域稀释型(轻度负向)
+**定义**:延伸词稀释原始问题的聚焦度,降低内容针对性
+
+**得分范围**:-0.08~-0.18
+
+**判定标准**:
+- 引入无关信息,分散注意力
+- 降低内容的专注度和深度
+- 使sug词条偏离原始问题的核心
+
+**示例**:
+- 原始:"专业风光摄影技巧" + 延伸词:"手机拍照"
+  - 评分:-0.12
+  - 理由:手机拍照与专业摄影需求不符,稀释专业度
+
+- 原始:"川西深度游攻略" + 延伸词:"周边一日游"
+  - 评分:-0.10
+  - 理由:一日游与深度游定位冲突,稀释深度
+
+
+---
+
+# 特殊情况处理
+
+## 情况1:多个延伸词同时存在
+**处理方法**:分别评估每个延伸词,然后综合
+
+**综合规则**:
+```
+延伸词总得分 = Σ(每个延伸词得分) / 延伸词数量
+
+考虑累积效应:
+- 多个增强型延伸词 → 总分可能超过单个最高分,但上限+0.25
+- 正负延伸词并存 → 相互抵消
+- 多个冲突型延伸词 → 总分下限-0.60
+```
+
+**示例**:
+```
+原始:"川西旅行行程"
+Sug词条:"川西旅行行程住宿美食推荐"
+延伸词识别:
+- "住宿推荐"→ 增强型,+0.18
+- "美食推荐"→ 辅助型,+0.10
+总得分:(0.18 + 0.10) / 2 = 0.14
+```
+
+---
+
+## 情况2:无延伸词
+**处理方法**:
+```
+IF sug词条无延伸词:
+   延伸词得分 = 0
+   理由:"sug词条未引入延伸词,所有词汇均属于原始问题作用域范围"
+```
+
+---
+
+## 情况3:延伸词使sug词条更接近原始问题
+**特殊加成**:
+```
+IF 延伸词是原始问题隐含需求的显式化:
+   → 额外加成 +0.05
+```
+
+**示例**:
+```
+原始:"川西旅行" (隐含需要行程规划)
+Sug词条:"川西旅行行程规划"
+- "行程规划"可能被识别为延伸词,但它显式化了隐含需求
+- 给予额外加成
+```
+
+---
+
+# 输出格式
+输出结果必须为一个 **JSON 格式**,包含以下内容:
+```json
+{
+  "延伸词得分": "-1到1之间的小数",
+  "简要说明延伸词维度相关度理由": "评估延伸词对作用域的影响"
+}
+```
+
+**输出约束(非常重要)**:
+1. **字符串长度限制**:\"简要说明延伸词维度相关度理由\"字段必须控制在**150字以内**
+2. **JSON格式规范**:必须生成完整的JSON格式,确保字符串用双引号包裹且正确闭合
+3. **引号使用**:字符串中如需表达引用,请使用《》或「」代替单引号或双引号
+
+---
+
+# 核心原则总结
+
+1. **严格区分**:作用域内的词 ≠ 延伸词
+2. **同义词/细化词不算延伸**:属于作用域范围的词由其他prompt评估
+3. **作用域导向**:评估延伸词是否使sug词条更接近原始问题的完整作用域
+4. **目的导向**:评估延伸词是否促进核心目的达成
+5. **分类明确**:准确判定延伸词类型
+6. **理由充分**:每个延伸词都要说明其对作用域和目的的影响
+7. **谨慎负分**:仅在明确冲突或有害时使用负分
+""".strip()
+
+# 创建评估 Agent
+motivation_evaluator = Agent[None](
+    name="动机维度评估专家(后续轮次)",
+    instructions=motivation_evaluation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=MotivationEvaluation)
+
+category_evaluator = Agent[None](
+    name="品类维度评估专家",
+    instructions=category_evaluation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=CategoryEvaluation
+)
+
+extension_word_evaluator = Agent[None](
+    name="延伸词评估专家",
+    instructions=extension_word_evaluation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=ExtensionWordEvaluation,
+    model_settings=ModelSettings(temperature=0.2)
+)
+
+
+# ============================================================================
+# Round 0 专用 Agent(v124新增 - 需求1)
+# ============================================================================
+
+# Round 0 动机评估 prompt(不含延伸词)
+round0_motivation_evaluation_instructions = """
+#角色
+你是**专业的动机意图评估专家**
+你的任务是:判断我给你的 <词条> 与 <原始问题> 的需求动机匹配度,给出 **-1 到 1 之间** 的数值评分。
+
+---
+# 输入信息
+你将接收到以下输入:
+- **<原始问题>**:用户的初始查询问题,代表用户的真实需求意图。
+- **<词条>**:平台推荐的词条列表,每个词条需要单独评估。
+
+# 核心约束
+
+## 维度独立性声明
+【严格约束】本评估**仅评估动机意图维度**:
+- **只评估** 用户"想要做什么",即原始问题的行为意图和目的
+- 核心是 **动词**:获取、学习、拍摄、制作、寻找等
+- 包括:核心动作 + 使用场景 + 最终目的
+- **评估重点**:动作本身及其语义方向
+ **禁止使用"主题相关"作为评分依据**:评分理由中不得出现"主题"、"内容"、"话题"等词
+
+---
+
+# 作用域与动作意图
+
+## 什么是作用域?
+**作用域 = 动机层 + 对象层 + 场景层**
+
+## 动作意图的识别
+
+### 方法1: 显性动词直接提取
+
+当原始问题明确包含动词时,直接提取
+示例:
+"如何获取素材" → 核心动机 = "获取"
+"寻找拍摄技巧" → 核心动机 = "寻找"(或"学习")
+"制作视频教程" → 核心动机 = "制作"
+
+### 方法2: 隐性动词语义推理
+当原始问题没有显性动词时,需要结合上下文推理
+
+如果原始问题是纯名词短语,无任何动作线索:
+→ 核心动机 = 无法识别
+→ 在此情况下,动机维度得分应为 0。
+示例:
+"摄影" → 无法识别动机,动机维度得分 = 0
+"川西风光" → 无法识别动机,动机维度得分 = 0
+
+---
+
+# 部分作用域的处理
+
+## 情况1:词条是原始问题的部分作用域
+
+当词条只包含原始问题的部分作用域时,需要判断:
+1. 词条是否包含动作意图
+2. 如果包含,动作是否匹配
+
+**示例**:
+```
+原始问题:"川西旅行行程规划"
+- 完整作用域:规划(动作)+ 旅行行程(对象)+ 川西(场景)
+
+词条:"川西旅行"
+- 包含作用域:旅行(部分对象)+ 川西(场景)
+- 缺失作用域:规划(动作)
+- 动作意图评分:0(无动作意图)
+```
+
+**评分原则**:
+- 如果sug词条缺失动机层(动作) → 动作意图得分 = 0
+- 如果sug词条包含动机层 → 按动作匹配度评分
+
+
+---
+
+#评分标准:
+
+【正向匹配】
+### +0.9~1.0:核心动作完全一致
+**示例**:
+- "规划旅行行程" vs "安排旅行路线" → 0.98
+  - 规划≈安排,语义完全一致
+- "获取素材" vs "下载素材" → 0.97
+  - 获取≈下载,语义完全一致
+
+- 特殊规则: 如果sug词的核心动作是原始问题动作的**具体化子集**,也判定为完全一致
+例: 原始问题"扣除猫咪主体的方法" vs 词条"扣除猫咪眼睛的方法"(子集但目的一致
+**注意**:此处不考虑对象和场景是否一致,只看动作本身
+
+###+0.75~0.90: 核心动作语义相近或为同义表达
+  - 例: 原始问题"如何获取素材" vs 词条"如何下载素材"
+  - 同义词对: 获取≈下载≈寻找, 技巧≈方法≈教程≈攻略
+
+### +0.50~0.75:动作意图相关
+**判定标准**:
+- 动作是实现原始意图的相关路径
+- 或动作是原始意图的前置/后置步骤
+
+**示例**:
+- "获取素材" vs "管理素材" → 0.65
+  - 管理是获取后的相关步骤
+- "规划行程" vs "预订酒店" → 0.60
+  - 预订是规划的具体实施步骤
+
+### +0.25~0.50:动作意图弱相关
+**判定标准**:
+- 动作在同一大类但方向不同
+- 或动作有间接关联
+
+**示例**:
+- "学习摄影技巧" vs "欣赏摄影作品" → 0.35
+  - 都与摄影有关,但学习≠欣赏
+- "规划旅行" vs "回忆旅行" → 0.30
+  - 都与旅行有关,但方向不同
+
+---
+
+## 【中性/无关】
+
+### 0:无动作意图或动作完全无关
+**适用场景**:
+1. 原始问题或词条无法识别动作
+2. 两者动作意图完全无关
+
+**示例**:
+- "如何获取素材" vs "摄影器材" → 0
+  - sug词条无动作意图
+- "川西风光" vs "风光摄影作品" → 0
+  - 原始问题无动作意图
+
+**理由模板**:
+- "sug词条无明确动作意图,无法评估动作匹配度"
+- "原始问题无明确动作意图,动作维度得分为0"
+
+---
+
+## 【负向偏离】
+
+### -0.2~-0.05:动作方向轻度偏离
+**示例**:
+- "学习摄影技巧" vs "销售摄影课程" → -0.10
+  - 学习 vs 销售,方向有偏差
+
+### -0.5~-0.25:动作意图明显冲突
+**示例**:
+- "获取免费素材" vs "购买素材" → -0.35
+  - 获取免费 vs 购买,明显冲突
+
+### -1.0~-0.55:动作意图完全相反
+**示例**:
+- "下载素材" vs "上传素材" → -0.70
+  - 下载 vs 上传,方向完全相反
+
+---
+
+# 输出要求
+
+输出结果必须为一个 **JSON 格式**,包含以下内容:
+```json
+{
+  "原始问题核心动机提取": {
+    "简要说明核心动机": ""
+  },
+  "动机维度得分": "-1到1之间的小数",
+  "简要说明动机维度相关度理由": "评估该词条与原始问题动机匹配程度的理由"
+}
+```
+
+#注意事项:
+始终围绕动机维度:所有评估都基于"动机"维度,不偏离
+核心动机必须是动词:在评估前,必须先提取原始问题的核心动机(动词),这是整个评估的基础
+严格标准一致性:对所有用例使用相同的评估标准,避免评分飘移
+负分使用原则:仅当词条对原始问题动机产生误导、冲突或有害引导时给予负分
+零分使用原则:当词条与原始问题动机无明确关联,既不相关也不冲突时给予零分,或原始问题无法识别动机时。
+""".strip()
+
+# Round 0 品类评估 prompt(不含延伸词)
+round0_category_evaluation_instructions = """
+#角色
+你是一个 **专业的语言专家和语义相关性评判专家**。
+你的任务是:判断我给你的 <词条> 与 <原始问题> 的内容主体和限定词匹配度,给出 **-1 到 1 之间** 的数值评分。
+
+---
+# 核心概念与方法论
+
+## 评估维度
+本评估系统围绕 **品类维度** 进行:
+
+#  维度独立性警告
+【严格约束】本评估**只评估品类维度**,,必须遵守以下规则:
+1. **只看名词和限定词**:评估时只考虑主体、限定词的匹配度
+2. **完全忽略动词**:动作意图、目的等动机信息对本维度评分无影响
+
+### 品类维度
+**定义:** 用户"关于什么内容",即原始问题的主题对象和限定词
+- 核心是 **名词+限定词**:川西秋季风光摄影素材
+- 包括:核心主体 + 地域限定 + 时间限定 + 质量限定等
+
+## ⚠️ 品类评估核心原则(必读)
+
+### 原则1:只看词条表面,禁止联想推演
+- 只能基于词条实际包含的词汇评分
+- 禁止推测"可能包含"、"可以理解为"
+
+**错误示例:**
+原始问题:"川西旅行行程" vs 词条:"每日计划"
+- 错误 "每日计划可以包含旅行规划,所以有关联" → 这是不允许的联想
+- 正确: "词条只有'每日计划',无'旅行'字眼,品类不匹配" → 正确判断
+
+### 原则2:通用概念 ≠ 特定概念
+- **通用**:计划、方法、技巧、素材(无领域限定)
+- **特定**:旅行行程、摄影技巧、烘焙方法(有明确领域)
+
+IF 词条是通用 且 原始问题是特定:
+   → 品类不匹配 → 评分0.05~0.1
+关键:通用概念不等于特定概念,不能因为"抽象上都是规划"就给分
+
+---
+
+# 输入信息
+你将接收到以下输入:
+- **<原始问题>**:用户的初始查询问题,代表用户的真实需求意图。
+- **<词条>**:平台推荐的词条列表,每个词条需要单独评估。
+
+
+#判定流程
+#评估架构
+
+输入: <原始问题> + <词条>
+         ↓
+【品类维度相关性判定】
+    ├→ 步骤1: 评估<词条>与<原始问题>的内容主体和限定词匹配度
+    └→ 输出: -1到1之间的数值 + 判定依据
+
+
+相关度评估维度详解
+维度2: 品类维度评估
+评估对象: <词条> 与 <原始问题> 的内容主体和限定词匹配度
+
+评分标准:
+
+【正向匹配】
++0.95~1.0: 核心主体+所有关键限定词完全匹配
+  - 例: 原始问题"川西秋季风光摄影素材" vs 词条"川西秋季风光摄影作品"
+
++0.75~0.95: 核心主体匹配,存在限定词匹配
+  - 例: 原始问题"川西秋季风光摄影素材" vs 词条"川西风光摄影素材"(缺失"秋季")
+
++0.5~0.75: 核心主体匹配,无限定词匹配或合理泛化
+  - 例: 原始问题"川西秋季风光摄影素材" vs 词条"四川风光摄影"
+
++0.3~0.5: 核心主体匹配,但限定词缺失或存在语义错位
+  - 特别注意"语义身份"差异,主体词出现但上下文语义不同
+  - 例:
+    · "猫咪的XX行为"(猫咪是行为者)
+    · vs "用猫咪表达XX的梗图"(猫咪是媒介)
+    · 虽都含"猫咪+XX",但语义角色不同
+
++0.2~0.3: 主体词不匹配,限定词缺失或错位
+  - 例: 原始问题"川西秋季风光摄影素材" vs 词条"风光摄影入门"
+
++0.05~0.2: 主体词过度泛化或仅抽象相似
+  - 例: 词条是通用概念,原始问题是特定概念
+    词条"每日计划"(通用)vs 原始问题 "川西旅行行程"(特定)
+      → 评分:0.08
+
+【中性/无关】
+0: 类别明显不同,没有明确目的,无明确关联
+  - 例: 原始问题"川西秋季风光摄影素材" vs 词条"人像摄影素材"
+  - 例: 原始问题无法识别动机 且 词条也无明确动作 → 0
+
+【负向偏离】
+-0.2~-0.05: 主体词或限定词存在误导性
+  - 例: 原始问题"免费摄影素材" vs 词条"付费摄影素材库"
+
+-0.5~-0.25: 主体词明显错位或品类冲突
+  - 例: 原始问题"风光摄影素材" vs 词条"人像修图教程"
+
+-1.0~-0.55: 完全错误的品类或有害引导
+  - 例: 原始问题"正版素材获取" vs 词条"盗版素材下载"
+
+---
+
+# 输出要求
+
+输出结果必须为一个 **JSON 格式**,包含以下内容:
+```json
+{
+  "品类维度得分": "-1到1之间的小数",
+  "简要说明品类维度相关度理由": "评估该词条与原始问题品类匹配程度的理由"
+}
+```
+---
+
+#注意事项:
+始终围绕品类维度:所有评估都基于"品类"维度,不偏离
+严格标准一致性:对所有用例使用相同的评估标准,避免评分飘移
+负分使用原则:仅当词条对原始问题品类产生误导、冲突或有害引导时给予负分
+零分使用原则:当词条与原始问题品类无明确关联,既不相关也不冲突时给予零分
+""".strip()
+
+# 创建 Round 0 评估 Agent
+round0_motivation_evaluator = Agent[None](
+    name="Round 0动机维度评估专家",
+    instructions=round0_motivation_evaluation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=MotivationEvaluation,
+    model_settings=ModelSettings(temperature=0.2)
+)
+
+round0_category_evaluator = Agent[None](
+    name="Round 0品类维度评估专家",
+    instructions=round0_category_evaluation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=CategoryEvaluation,
+    model_settings=ModelSettings(temperature=0.2)
+)
+
+
+# ============================================================================
+# 域内/域间 专用 Agent(v124新增 - 需求2&3)
+# ============================================================================
+
+# 域内/域间 动机评估 prompt(不含延伸词)
+scope_motivation_evaluation_instructions = """
+# 角色
+你是**专业的动机意图评估专家**。
+任务:判断<词条>与<同一作用域词条>的**动机意图匹配度**,给出**-1到1之间**的数值评分。
+
+---
+# 输入信息
+你将接收到以下输入:
+ **<同一作用域词条>**:用户的初始查询问题,代表用户的真实需求意图。
+- **<词条>**:平台推荐的词条列表,每个词条需要单独评估。
+---
+# 评估架构
+
+输入: <同一作用域词条> + <词条>
+         ↓
+【动机维度相关性判定】
+    ├→ 步骤1: 评估<词条>与<同一作用域词条>的需求动机匹配度
+    └→ 输出: -1到1之间的数值 + 判定依据
+
+# 核心约束
+## 维度独立性声明
+【严格约束】本评估**仅评估动机意图维度**:
+- **只评估** 用户"想要做什么",即原始问题的行为意图和目的
+- 核心是 **动词**:获取、学习、拍摄、制作、寻找等
+- 包括:核心动作 + 使用场景 + 最终目的
+- **评估重点**:动作本身及其语义方向
+ **禁止使用"主题相关"作为评分依据**:评分理由中不得出现"主题"、"内容"、"话题"等词
+
+---
+
+# 作用域与动作意图
+
+## 什么是作用域?
+**作用域 = 动机层 + 对象层 + 场景层**
+
+当前任务:
+- **只提取动机层**:动作意图(获取、学习、规划、拍摄等)
+
+## 动作意图的识别
+
+### 1. 动机维度
+**定义:** 用户"想要做什么",即原始问题的行为意图和目的
+- 核心是 **动词**:获取、学习、拍摄、制作、寻找等
+- 包括:核心动作 + 使用场景 + 最终目的
+
+### 方法1: 显性动词直接提取
+
+当原始问题明确包含动词时,直接提取
+示例:
+"如何获取素材" → 核心动机 = "获取"
+"寻找拍摄技巧" → 核心动机 = "寻找"(或"学习")
+"制作视频教程" → 核心动机 = "制作"
+
+### 方法2: 隐性动词语义推理
+当原始问题没有显性动词时,需要结合上下文推理
+
+
+---
+
+# 评分标准
+
+## 【正向匹配】
+
+### +0.9~1.0:核心动作完全一致
+**示例**:
+- "规划旅行行程" vs "安排旅行路线" → 0.98
+  - 规划≈安排,语义完全一致
+- "获取素材" vs "下载素材" → 0.97
+  - 获取≈下载,语义完全一致
+
+- 特殊规则: 如果sug词的核心动作是原始问题动作的**具体化子集**,也判定为完全一致
+例: 原始问题"扣除猫咪主体的方法" vs sug词"扣除猫咪眼睛的方法"(子集但目的一致
+**注意**:此处不考虑对象和场景是否一致,只看动作本身
+
+###+0.75~0.95: 核心动作语义相近或为同义表达
+  - 例: 原始问题"如何获取素材" vs sug词"如何下载素材"
+  - 同义词对: 获取≈下载≈寻找, 技巧≈方法≈教程≈攻略
+
+### +0.50~0.75:动作意图相关
+**判定标准**:
+- 动作是实现原始意图的相关路径
+- 或动作是原始意图的前置/后置步骤
+
+**示例**:
+- "获取素材" vs "管理素材" → 0.65
+  - 管理是获取后的相关步骤
+- "规划行程" vs "预订酒店" → 0.60
+  - 预订是规划的具体实施步骤
+
+### +0.25~0.50:动作意图弱相关
+**判定标准**:
+- 动作在同一大类但方向不同
+- 或动作有间接关联
+
+**示例**:
+- "学习摄影技巧" vs "欣赏摄影作品" → 0.35
+  - 都与摄影有关,但学习≠欣赏
+- "规划旅行" vs "回忆旅行" → 0.30
+  - 都与旅行有关,但方向不同
+
+---
+
+## 【中性/无关】
+
+### 0:无动作意图或动作完全无关
+**适用场景**:
+1. 原始问题或词条无法识别动作
+2. 两者动作意图完全无关
+
+**示例**:
+- "如何获取素材" vs "摄影器材" → 0
+  - 词条无动作意图
+- "川西风光" vs "风光摄影作品" → 0
+  - 原始问题无动作意图
+
+**理由模板**:
+- "词条无明确动作意图,无法评估动作匹配度"
+- "原始问题无明确动作意图,动作维度得分为0"
+
+---
+
+## 【负向偏离】
+
+### -0.2~-0.05:动作方向轻度偏离
+**示例**:
+- "学习摄影技巧" vs "销售摄影课程" → -0.10
+  - 学习 vs 销售,方向有偏差
+
+### -0.5~-0.25:动作意图明显冲突
+**示例**:
+- "获取免费素材" vs "购买素材" → -0.35
+  - 获取免费 vs 购买,明显冲突
+
+### -1.0~-0.55:动作意图完全相反
+**示例**:
+- "下载素材" vs "上传素材" → -0.70
+  - 下载 vs 上传,方向完全相反
+
+---
+
+# 输出格式
+输出结果必须为一个 **JSON 格式**,包含以下内容:
+```json
+{
+  "原始问题核心动机提取": {
+    "简要说明核心动机": ""
+  },
+  "动机维度得分": "-1到1之间的小数",
+  "简要说明动机维度相关度理由": "评估该词条与该条作用域匹配程度的理由",
+  "得分为零的原因": "原始问题无动机/sug词条无动机/动机不匹配/不适用"
+}
+```
+
+---
+
+# 核心原则总结
+1. **只评估动作**:完全聚焦于动作意图,不管对象和场景
+2. **作用域识别**:识别作用域但只评估动机层
+3. **严格标准一致性**:对所有用例使用相同的评估标准,避免评分飘移
+4. **理由纯粹**:评分理由只能谈动作,不能谈对象、场景、主题
+""".strip()
+
+# 域内/域间 品类评估 prompt(不含延伸词)
+scope_category_evaluation_instructions = """
+#角色
+你是一个 **专业的语言专家和语义相关性评判专家**。
+你的任务是:判断我给你的 <词条> 与 <同一作用域词条> 的内容主体和限定词匹配度,给出 **-1 到 1 之间** 的数值评分。
+
+---
+# 输入信息
+你将接收到以下输入:
+- **<同一作用域词条>**:用户的初始查询问题,代表用户的真实需求意图。
+- **<词条>**:平台推荐的词条列表,每个词条需要单独评估。
+
+---
+#判定流程
+#评估架构
+
+输入: <同一作用域词条> + <词条>
+         ↓
+【品类维度相关性判定】
+    ├→ 步骤1: 评估<词条>与<同一作用域词条>的内容主体和限定词匹配度
+    └→ 输出: -1到1之间的数值 + 判定依据
+
+---
+
+# 核心概念与方法论
+
+## 评估维度
+本评估系统围绕 **品类维度** 进行:
+
+#  维度独立性警告
+【严格约束】本评估**只评估品类维度**,,必须遵守以下规则:
+1. **只看名词和限定词**:评估时只考虑主体、限定词的匹配度
+2. **完全忽略动词**:动作意图、目的等动机信息对本维度评分无影响
+
+### 品类维度
+**定义:** 用户"关于什么内容",即原始问题的主题对象和限定词
+- 核心是 **名词+限定词**:川西秋季风光摄影素材
+- 包括:核心主体 + 地域限定 + 时间限定 + 质量限定等
+
+## ⚠️ 品类评估核心原则(必读)
+
+### 原则1:只看词条表面,禁止联想推演
+- 只能基于sug词实际包含的词汇评分
+- 禁止推测"可能包含"、"可以理解为"
+
+**错误示例:**
+原始问题:"川西旅行行程" vs sug词:"每日计划"
+- 错误 "每日计划可以包含旅行规划,所以有关联" → 这是不允许的联想
+- 正确: "sug词只有'每日计划',无'旅行'字眼,品类不匹配" → 正确判断
+
+### 原则2:通用概念 ≠ 特定概念
+- **通用**:计划、方法、技巧、素材(无领域限定)
+- **特定**:旅行行程、摄影技巧、烘焙方法(有明确领域)
+
+IF sug词是通用 且 原始问题是特定:
+   → 品类不匹配 → 评分0.05~0.1
+关键:通用概念不等于特定概念,不能因为"抽象上都是规划"就给分
+
+---
+#相关度评估维度详解
+
+##评估对象: <词条> 与 <同一作用域词条> 的内容主体和限定词匹配度
+
+评分标准:
+
+【正向匹配】
++0.95~1.0: 核心主体+所有关键限定词完全匹配
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"川西秋季风光摄影作品"
+
++0.75~0.95: 核心主体匹配,存在限定词匹配
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"川西风光摄影素材"(缺失"秋季")
+
++0.5~0.75: 核心主体匹配,无限定词匹配或合理泛化
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"四川风光摄影"
+
++0.3~0.5: 核心主体匹配,但限定词缺失或存在语义错位
+  - 特别注意"语义身份"差异,主体词出现但上下文语义不同
+  - 例:
+    · "猫咪的XX行为"(猫咪是行为者)
+    · vs "用猫咪表达XX的梗图"(猫咪是媒介)
+    · 虽都含"猫咪+XX",但语义角色不同
+
++0.2~0.3: 主体词不匹配,限定词缺失或错位
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"风光摄影入门"
+
++0.05~0.2: 主体词过度泛化或仅抽象相似
+  - 例: sug词是通用概念,原始问题是特定概念
+    sug词"每日计划"(通用)vs 原始问题 "川西旅行行程"(特定)
+      → 评分:0.08
+
+【中性/无关】
+0: 类别明显不同,没有明确目的,无明确关联
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"人像摄影素材"
+  - 例: 原始问题无法识别动机 且 sug词也无明确动作 → 0
+
+【负向偏离】
+-0.2~-0.05: 主体词或限定词存在误导性
+  - 例: 原始问题"免费摄影素材" vs sug词"付费摄影素材库"
+
+-0.5~-0.25: 主体词明显错位或品类冲突
+  - 例: 原始问题"风光摄影素材" vs sug词"人像修图教程"
+
+-1.0~-0.55: 完全错误的品类或有害引导
+  - 例: 原始问题"正版素材获取" vs sug词"盗版素材下载"
+
+---
+
+# 输出要求
+
+输出结果必须为一个 **JSON 格式**,包含以下内容:
+```json
+{
+  "品类维度得分": "-1到1之间的小数",
+  "简要说明品类维度相关度理由": "评估该词条与同一作用域词条品类匹配程度的理由"
+}
+```
+---
+
+#注意事项:
+始终围绕品类维度:所有评估都基于"品类"维度,不偏离
+严格标准一致性:对所有用例使用相同的评估标准,避免评分飘移
+负分使用原则:仅当词条对原始问题品类产生误导、冲突或有害引导时给予负分
+零分使用原则:当词条与原始问题品类无明确关联,既不相关也不冲突时给予零分
+""".strip()
+
+# 创建域内/域间评估 Agent
+scope_motivation_evaluator = Agent[None](
+    name="域内动机维度评估专家",
+    instructions=scope_motivation_evaluation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=MotivationEvaluation,
+   model_settings=ModelSettings(temperature=0.2)
+)
+
+scope_category_evaluator = Agent[None](
+    name="域内品类维度评估专家",
+    instructions=scope_category_evaluation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=CategoryEvaluation,
+    model_settings=ModelSettings(temperature=0.2)
+)
+
+
+# ============================================================================
+# v120 保留但不使用的 Agent(v121不再使用)
+# ============================================================================
+
+# # Agent 3: 加词选择专家(旧版 - v120使用,v121不再使用)
+# class WordCombination(BaseModel):
+#     """单个词组合"""
+#     selected_word: str = Field(..., description="选择的词")
+#     combined_query: str = Field(..., description="组合后的新query")
+#     reasoning: str = Field(..., description="选择理由")
+
+# class WordSelectionTop5(BaseModel):
+#     """加词选择结果(Top 5)"""
+#     combinations: list[WordCombination] = Field(
+#         ...,
+#         description="选择的Top 5组合(不足5个则返回所有)",
+#         min_items=1,
+#         max_items=5
+#     )
+#     overall_reasoning: str = Field(..., description="整体选择思路")
+
+# word_selection_instructions 已删除 (v121不再使用)
+
+# word_selector = Agent[None](
+#     name="加词组合专家",
+#     instructions=word_selection_instructions,
+#     model=get_model(MODEL_NAME),
+#     output_type=WordSelectionTop5,
+#     model_settings=ModelSettings(temperature=0.2),
+# )
+
+
+# ============================================================================
+# 辅助函数
+# ============================================================================
+
+# ============================================================================
+# v121 新增辅助函数
+# ============================================================================
+
+def get_ordered_subsets(words: list[str], min_len: int = 1) -> list[list[str]]:
+    """
+    生成words的所有有序子集(可跳过但不可重排)
+
+    使用 itertools.combinations 生成索引组合,保持原始顺序
+
+    Args:
+        words: 词列表
+        min_len: 子集最小长度
+
+    Returns:
+        所有可能的有序子集列表
+
+    Example:
+        words = ["川西", "秋季", "风光"]
+        结果:
+        - 长度1: ["川西"], ["秋季"], ["风光"]
+        - 长度2: ["川西", "秋季"], ["川西", "风光"], ["秋季", "风光"]
+        - 长度3: ["川西", "秋季", "风光"]
+        共 C(3,1) + C(3,2) + C(3,3) = 3 + 3 + 1 = 7种
+    """
+    from itertools import combinations
+
+    subsets = []
+    n = len(words)
+
+    # 遍历所有可能的长度(从min_len到n)
+    for r in range(min_len, n + 1):
+        # 生成长度为r的所有索引组合
+        for indices in combinations(range(n), r):
+            # 按照原始顺序提取词
+            subset = [words[i] for i in indices]
+            subsets.append(subset)
+
+    return subsets
+
+
+def generate_domain_combinations(segments: list[Segment], n_domains: int) -> list[DomainCombination]:
+    """
+    生成N域组合
+
+    步骤:
+    1. 从len(segments)个域中选择n_domains个域(组合,保持顺序)
+    2. 对每个选中的域,生成其words的所有有序子集
+    3. 计算笛卡尔积,生成所有可能的组合
+
+    Args:
+        segments: 语义片段列表
+        n_domains: 参与组合的域数量
+
+    Returns:
+        所有可能的N域组合列表
+
+    Example:
+        有4个域: [疑问标记, 核心动作, 修饰短语, 中心名词]
+        n_domains=2时,选择域的方式: C(4,2) = 6种
+
+        假设选中[核心动作, 中心名词]:
+        - 核心动作的words: ["获取"], 子集: ["获取"]
+        - 中心名词的words: ["风光", "摄影", "素材"], 子集: 7种
+        则该域选择下的组合数: 1 * 7 = 7种
+    """
+    from itertools import combinations, product
+
+    all_combinations = []
+    n = len(segments)
+
+    # 检查参数有效性
+    if n_domains > n or n_domains < 1:
+        return []
+
+    # 1. 选择n_domains个域(保持原始顺序)
+    for domain_indices in combinations(range(n), n_domains):
+        selected_segments = [segments[i] for i in domain_indices]
+
+        # 新增:如果所有域都只有1个词,跳过(单段落单词不组合)
+        if all(len(seg.words) == 1 for seg in selected_segments):
+            continue
+
+        # 2. 为每个选中的域生成其words的所有有序子集
+        domain_subsets = []
+        for seg in selected_segments:
+            if len(seg.words) == 0:
+                # 如果某个域没有词,跳过该域组合
+                domain_subsets = []
+                break
+            subsets = get_ordered_subsets(seg.words, min_len=1)
+            domain_subsets.append(subsets)
+
+        # 如果某个域没有词,跳过
+        if len(domain_subsets) != n_domains:
+            continue
+
+        # 3. 计算笛卡尔积
+        for word_combination in product(*domain_subsets):
+            # word_combination 是一个tuple,每个元素是一个词列表
+            # 例如: (["获取"], ["风光", "摄影"])
+
+            # 计算总词数
+            total_words = sum(len(words) for words in word_combination)
+
+            # 如果总词数<=1,跳过(组词必须大于1个词)
+            if total_words <= 1:
+                continue
+
+            # 将所有词连接成一个字符串
+            combined_text = "".join(["".join(words) for words in word_combination])
+
+            # 生成类型标签
+            type_labels = [selected_segments[i].type for i in range(n_domains)]
+            type_label = "[" + "+".join(type_labels) + "]"
+
+            # 创建DomainCombination对象
+            comb = DomainCombination(
+                text=combined_text,
+                domains=list(domain_indices),
+                type_label=type_label,
+                source_words=[list(words) for words in word_combination],  # 保存来源词
+                from_segments=[seg.text for seg in selected_segments]
+            )
+            all_combinations.append(comb)
+
+    return all_combinations
+
+
+def extract_words_from_segments(segments: list[Segment]) -> list[Q]:
+    """
+    从 segments 中提取所有 words,转换为 Q 对象列表
+
+    用于 Round 1 的输入:将 Round 0 的 words 转换为可用于请求SUG的 query 列表
+
+    Args:
+        segments: Round 0 的语义片段列表
+
+    Returns:
+        list[Q]: word 列表,每个 word 作为一个 Q 对象
+    """
+    q_list = []
+
+    for seg_idx, segment in enumerate(segments):
+        for word in segment.words:
+            # 从 segment.word_scores 获取该 word 的评分
+            word_score = segment.word_scores.get(word, 0.0)
+            word_reason = segment.word_reasons.get(word, "")
+
+            # 创建 Q 对象
+            q = Q(
+                text=word,
+                score_with_o=word_score,
+                reason=word_reason,
+                from_source="word",  # 标记来源为 word
+                type_label=f"[{segment.type}]",  # 保留域信息
+                domain_index=seg_idx,  # 添加域索引
+                domain_type=segment.type  # 添加域类型(如"中心名词"、"核心动作")
+            )
+            q_list.append(q)
+
+    return q_list
+
+
+# ============================================================================
+# v120 保留辅助函数
+# ============================================================================
+
+def calculate_final_score(
+    motivation_score: float,
+    category_score: float,
+    extension_score: float,
+    zero_reason: Optional[str],
+    extension_reason: str = ""
+) -> tuple[float, str]:
+    """
+    三维评估综合打分
+
+    实现动态权重分配:
+    - 情况1:标准情况 → 动机50% + 品类40% + 延伸词10%
+    - 情况2:原始问题无动机 → 品类70% + 延伸词30%
+    - 情况3:sug词条无动机 → 品类80% + 延伸词20%
+    - 情况4:无延伸词 → 动机70% + 品类30%
+    - 规则3:负分传导 → 核心维度严重负向时上限为0
+    - 规则4:完美匹配加成 → 双维度≥0.95时加成+0.10
+
+    Args:
+        motivation_score: 动机维度得分 -1~1
+        category_score: 品类维度得分 -1~1
+        extension_score: 延伸词得分 -1~1
+        zero_reason: 当motivation_score=0时的原因(可选)
+        extension_reason: 延伸词评估理由,用于判断是否无延伸词
+
+    Returns:
+        (最终得分, 规则说明)
+    """
+
+    # 情况2:原始问题无动作意图
+    if motivation_score == 0 and zero_reason == "原始问题无动机":
+        W1, W2, W3 = 0.0, 0.70, 0.30
+        base_score = category_score * W2 + extension_score * W3
+        rule_applied = "情况2:原始问题无动作意图,权重调整为 品类70% + 延伸词30%"
+
+    # 情况3:sug词条无动作意图(但原始问题有)
+    elif motivation_score == 0 and zero_reason == "sug词条无动机":
+        W1, W2, W3 = 0.0, 0.80, 0.20
+        base_score = category_score * W2 + extension_score * W3
+        rule_applied = "情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%"
+
+    # 情况4:无延伸词
+    elif extension_score == 0:
+        W1, W2, W3 = 0.70, 0.30, 0.0
+        base_score = motivation_score * W1 + category_score * W2
+        rule_applied = "情况4:无延伸词,权重调整为 动机70% + 品类30%"
+
+    else:
+        # 情况1:标准权重
+        W1, W2, W3 = 0.50, 0.40, 0.10
+        base_score = motivation_score * W1 + category_score * W2 + extension_score * W3
+        rule_applied = ""
+
+    # 规则4:完美匹配加成
+    if motivation_score >= 0.95 and category_score >= 0.95:
+        base_score += 0.10
+        rule_applied += (" + " if rule_applied else "") + "规则4:双维度完美匹配,加成+0.10"
+
+    # 规则3:负分传导
+    if motivation_score <= -0.5 or category_score <= -0.5:
+        base_score = min(base_score, 0)
+        rule_applied += (" + " if rule_applied else "") + "规则3:核心维度严重负向,上限=0"
+
+    # 边界处理
+    final_score = max(-1.0, min(1.0, base_score))
+
+    return final_score, rule_applied
+
+
+def calculate_final_score_v2(
+    motivation_score: float,
+    category_score: float
+) -> tuple[float, str]:
+    """
+    两维评估综合打分(v124新增 - 需求1)
+
+    用于Round 0分词评估和域内/域间评估,不含延伸词维度
+
+    基础权重:动机70% + 品类30%
+
+    应用规则:
+    - 规则A:动机高分保护机制
+      IF 动机维度得分 ≥ 0.8:
+         品类得分即使为0或轻微负向(-0.2~0)
+         → 最终得分应该不低于0.7
+      解释: 当目的高度一致时,品类的泛化不应导致"弱相关"
+
+    - 规则B:动机低分限制机制
+      IF 动机维度得分 ≤ 0.2:
+         无论品类得分多高
+         → 最终得分不高于0.5
+      解释: 目的不符时,品类匹配的价值有限
+
+    - 规则C:动机负向决定机制
+      IF 动机维度得分 < 0:
+         → 最终得分为0
+      解释: 动作意图冲突时,推荐具有误导性,不应为正相关
+
+    Args:
+        motivation_score: 动机维度得分 -1~1
+        category_score: 品类维度得分 -1~1
+
+    Returns:
+        (最终得分, 规则说明)
+    """
+
+    rule_applied = ""
+
+    # 规则C:动机负向决定机制
+    if motivation_score < 0:
+        final_score = 0.0
+        rule_applied = "规则C:动机负向,最终得分=0"
+        return final_score, rule_applied
+
+    # 基础加权计算: 动机70% + 品类30%
+    base_score = motivation_score * 0.7 + category_score * 0.3
+
+    # 规则A:动机高分保护机制
+    if motivation_score >= 0.8:
+        if base_score < 0.7:
+            final_score = 0.7
+            rule_applied = f"规则A:动机高分保护(动机{motivation_score:.2f}≥0.8),最终得分下限=0.7"
+        else:
+            final_score = base_score
+            rule_applied = f"规则A:动机高分保护生效(动机{motivation_score:.2f}≥0.8),实际得分{base_score:.2f}已≥0.7"
+
+    # 规则B:动机低分限制机制
+    elif motivation_score <= 0.2:
+        if base_score > 0.5:
+            final_score = 0.5
+            rule_applied = f"规则B:动机低分限制(动机{motivation_score:.2f}≤0.2),最终得分上限=0.5"
+        else:
+            final_score = base_score
+            rule_applied = f"规则B:动机低分限制生效(动机{motivation_score:.2f}≤0.2),实际得分{base_score:.2f}已≤0.5"
+
+    # 无规则触发
+    else:
+        final_score = base_score
+        rule_applied = ""
+
+    # 边界处理
+    final_score = max(-1.0, min(1.0, final_score))
+
+    return final_score, rule_applied
+
+
+def clean_json_string(text: str) -> str:
+    """清理JSON中的非法控制字符(保留 \t \n \r)"""
+    import re
+    # 移除除了 \t(09) \n(0A) \r(0D) 之外的所有控制字符
+    return re.sub(r'[\x00-\x08\x0B\x0C\x0E-\x1F]', '', text)
+
+
+def process_note_data(note: dict) -> Post:
+    """处理搜索接口返回的帖子数据"""
+    note_card = note.get("note_card", {})
+    image_list = note_card.get("image_list", [])
+    interact_info = note_card.get("interact_info", {})
+    user_info = note_card.get("user", {})
+
+    # ========== 调试日志 START ==========
+    note_id = note.get("id", "")
+    raw_title = note_card.get("display_title")  # 不提供默认值
+    raw_body = note_card.get("desc")
+    raw_type = note_card.get("type")
+
+    # 打印原始值类型和内容
+    print(f"\n[DEBUG] 处理帖子 {note_id}:")
+    print(f"  raw_title 类型: {type(raw_title).__name__}, 值: {repr(raw_title)}")
+    print(f"  raw_body 类型: {type(raw_body).__name__}, 值: {repr(raw_body)[:100] if raw_body else repr(raw_body)}")
+    print(f"  raw_type 类型: {type(raw_type).__name__}, 值: {repr(raw_type)}")
+
+    # 检查是否为 None
+    if raw_title is None:
+        print(f"  ⚠️  WARNING: display_title 是 None!")
+    if raw_body is None:
+        print(f"  ⚠️  WARNING: desc 是 None!")
+    if raw_type is None:
+        print(f"  ⚠️  WARNING: type 是 None!")
+    # ========== 调试日志 END ==========
+
+    # 提取图片URL - 使用新的字段名 image_url
+    images = []
+    for img in image_list:
+        if isinstance(img, dict):
+            # 尝试新字段名 image_url,如果不存在则尝试旧字段名 url_default
+            img_url = img.get("image_url") or img.get("url_default")
+            if img_url:
+                images.append(img_url)
+
+    # 判断类型
+    note_type = note_card.get("type", "normal")
+    video_url = ""
+    if note_type == "video":
+        video_info = note_card.get("video", {})
+        if isinstance(video_info, dict):
+            # 尝试获取视频URL
+            video_url = video_info.get("media", {}).get("stream", {}).get("h264", [{}])[0].get("master_url", "")
+
+    return Post(
+        note_id=note.get("id") or "",
+        title=note_card.get("display_title") or "",
+        body_text=note_card.get("desc") or "",
+        type=note_type,
+        images=images,
+        video=video_url,
+        interact_info={
+            "liked_count": interact_info.get("liked_count", 0),
+            "collected_count": interact_info.get("collected_count", 0),
+            "comment_count": interact_info.get("comment_count", 0),
+            "shared_count": interact_info.get("shared_count", 0)
+        },
+        note_url=f"https://www.xiaohongshu.com/explore/{note.get('id', '')}"
+    )
+
+
+async def evaluate_with_o(text: str, o: str, cache: dict[str, tuple[float, str]] | None = None) -> tuple[float, str]:
+    """评估文本与原始问题o的相关度
+
+    采用两阶段评估 + 代码计算规则:
+    1. 动机维度评估(权重70%)
+    2. 品类维度评估(权重30%)
+    3. 应用规则A/B/C调整得分
+
+    Args:
+        text: 待评估的文本
+        o: 原始问题
+        cache: 评估缓存(可选),用于避免重复评估
+
+    Returns:
+        tuple[float, str]: (最终相关度分数, 综合评估理由)
+    """
+    # 检查缓存
+    if cache is not None and text in cache:
+        cached_score, cached_reason = cache[text]
+        print(f"  ⚡ 缓存命中: {text} -> {cached_score:.2f}")
+        return cached_score, cached_reason
+
+    # 准备输入
+    eval_input = f"""
+<原始问题>
+{o}
+</原始问题>
+
+<平台sug词条>
+{text}
+</平台sug词条>
+
+请评估平台sug词条与原始问题的匹配度。
+"""
+
+    # 添加重试机制
+    max_retries = 2
+    last_error = None
+
+    for attempt in range(max_retries):
+        try:
+            # 并发调用三个评估器
+            motivation_task = Runner.run(motivation_evaluator, eval_input)
+            category_task = Runner.run(category_evaluator, eval_input)
+            extension_task = Runner.run(extension_word_evaluator, eval_input)
+
+            motivation_result, category_result, extension_result = await asyncio.gather(
+                motivation_task,
+                category_task,
+                extension_task
+            )
+
+            # 获取评估结果
+            motivation_eval: MotivationEvaluation = motivation_result.final_output
+            category_eval: CategoryEvaluation = category_result.final_output
+            extension_eval: ExtensionWordEvaluation = extension_result.final_output
+
+            # 提取得分
+            motivation_score = motivation_eval.动机维度得分
+            category_score = category_eval.品类维度得分
+            extension_score = extension_eval.延伸词得分
+            zero_reason = motivation_eval.得分为零的原因
+
+            # 应用规则计算最终得分
+            final_score, rule_applied = calculate_final_score(
+                motivation_score, category_score, extension_score, zero_reason,
+                extension_eval.简要说明延伸词维度相关度理由
+            )
+
+            # 组合评估理由
+            core_motivation = motivation_eval.原始问题核心动机提取.简要说明核心动机
+            motivation_reason = motivation_eval.简要说明动机维度相关度理由
+            category_reason = category_eval.简要说明品类维度相关度理由
+            extension_reason = extension_eval.简要说明延伸词维度相关度理由
+
+            combined_reason = (
+                f'【评估对象】词条"{text}" vs 原始问题"{o}"\n'
+                f"【核心动机】{core_motivation}\n"
+                f"【动机维度 {motivation_score:.2f}】{motivation_reason}\n"
+                f"【品类维度 {category_score:.2f}】{category_reason}\n"
+                f"【延伸词维度 {extension_score:.2f}】{extension_reason}\n"
+                f"【最终得分 {final_score:.2f}】"
+            )
+
+            # 添加规则说明
+            if rule_applied:
+                combined_reason += f"\n【规则说明】{rule_applied}"
+
+            # 存入缓存
+            if cache is not None:
+                cache[text] = (final_score, combined_reason)
+
+            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
+
+
+async def evaluate_with_o_round0(text: str, o: str, cache: dict[str, tuple[float, str]] | None = None) -> tuple[float, str]:
+    """Round 0专用评估函数(v124新增 - 需求1)
+
+    用于评估segment和word与原始问题的相关度
+    不含延伸词维度,使用Round 0专用Prompt和新评分逻辑
+
+    采用两维评估:
+    1. 动机维度评估(权重70%)
+    2. 品类维度评估(权重30%)
+    3. 应用规则A/B/C调整得分
+
+    Args:
+        text: 待评估的文本(segment或word)
+        o: 原始问题
+        cache: 评估缓存(可选),用于避免重复评估
+
+    Returns:
+        tuple[float, str]: (最终相关度分数, 综合评估理由)
+    """
+    # 检查缓存
+    cache_key = f"round0:{text}:{o}"  # 添加前缀以区分不同评估类型
+    if cache is not None and cache_key in cache:
+        cached_score, cached_reason = cache[cache_key]
+        print(f"  ⚡ Round0缓存命中: {text} -> {cached_score:.2f}")
+        return cached_score, cached_reason
+
+    # 准备输入
+    eval_input = f"""
+<原始问题>
+{o}
+</原始问题>
+
+<词条>
+{text}
+</词条>
+
+请评估词条与原始问题的匹配度。
+"""
+
+    # 添加重试机制
+    max_retries = 2
+    last_error = None
+
+    for attempt in range(max_retries):
+        try:
+            # 并发调用两个评估器(不含延伸词)
+            motivation_task = Runner.run(round0_motivation_evaluator, eval_input)
+            category_task = Runner.run(round0_category_evaluator, eval_input)
+
+            motivation_result, category_result = await asyncio.gather(
+                motivation_task,
+                category_task
+            )
+
+            # 获取评估结果
+            motivation_eval: MotivationEvaluation = motivation_result.final_output
+            category_eval: CategoryEvaluation = category_result.final_output
+
+            # 提取得分
+            motivation_score = motivation_eval.动机维度得分
+            category_score = category_eval.品类维度得分
+
+            # 应用新规则计算最终得分
+            final_score, rule_applied = calculate_final_score_v2(
+                motivation_score, category_score
+            )
+
+            # 组合评估理由
+            core_motivation = motivation_eval.原始问题核心动机提取.简要说明核心动机
+            motivation_reason = motivation_eval.简要说明动机维度相关度理由
+            category_reason = category_eval.简要说明品类维度相关度理由
+
+            combined_reason = (
+                f'【评估对象】词条"{text}" vs 原始问题"{o}"\n'
+                f"【核心动机】{core_motivation}\n"
+                f"【动机维度 {motivation_score:.2f}】{motivation_reason}\n"
+                f"【品类维度 {category_score:.2f}】{category_reason}\n"
+                f"【最终得分 {final_score:.2f}】"
+            )
+
+            # 添加规则说明
+            if rule_applied:
+                combined_reason += f"\n【规则说明】{rule_applied}"
+
+            # 存入缓存
+            if cache is not None:
+                cache[cache_key] = (final_score, combined_reason)
+
+            return final_score, combined_reason
+
+        except Exception as e:
+            last_error = e
+            error_msg = str(e)
+
+            if attempt < max_retries - 1:
+                print(f"  ⚠️  Round0评估失败 (尝试 {attempt+1}/{max_retries}): {error_msg[:150]}")
+                print(f"  正在重试...")
+                await asyncio.sleep(1)
+            else:
+                print(f"  ❌ Round0评估失败 (已达最大重试次数): {error_msg[:150]}")
+
+    # 所有重试失败后,返回默认值
+    fallback_reason = f"Round0评估失败(重试{max_retries}次): {str(last_error)[:200]}"
+    print(f"  使用默认值: score=0.0, reason={fallback_reason[:100]}...")
+    return 0.0, fallback_reason
+
+
+async def evaluate_within_scope(text: str, scope_text: str, cache: dict[str, tuple[float, str]] | None = None) -> tuple[float, str]:
+    """域内/域间专用评估函数(v124新增 - 需求2&3)
+
+    用于评估词条与作用域词条(单域或域组合)的相关度
+    不含延伸词维度,使用域内专用Prompt和新评分逻辑
+
+    采用两维评估:
+    1. 动机维度评估(权重70%)
+    2. 品类维度评估(权重30%)
+    3. 应用规则A/B/C调整得分
+
+    Args:
+        text: 待评估的词条
+        scope_text: 作用域词条(可以是单域词条或域组合词条)
+        cache: 评估缓存(可选),用于避免重复评估
+
+    Returns:
+        tuple[float, str]: (最终相关度分数, 综合评估理由)
+    """
+    # 检查缓存
+    cache_key = f"scope:{text}:{scope_text}"  # 添加前缀以区分不同评估类型
+    if cache is not None and cache_key in cache:
+        cached_score, cached_reason = cache[cache_key]
+        print(f"  ⚡ 域内缓存命中: {text} -> {cached_score:.2f}")
+        return cached_score, cached_reason
+
+    # 准备输入
+    eval_input = f"""
+<同一作用域词条>
+{scope_text}
+</同一作用域词条>
+
+<词条>
+{text}
+</词条>
+
+请评估词条与同一作用域词条的匹配度。
+"""
+
+    # 添加重试机制
+    max_retries = 2
+    last_error = None
+
+    for attempt in range(max_retries):
+        try:
+            # 并发调用两个评估器(不含延伸词)
+            motivation_task = Runner.run(scope_motivation_evaluator, eval_input)
+            category_task = Runner.run(scope_category_evaluator, eval_input)
+
+            motivation_result, category_result = await asyncio.gather(
+                motivation_task,
+                category_task
+            )
+
+            # 获取评估结果
+            motivation_eval: MotivationEvaluation = motivation_result.final_output
+            category_eval: CategoryEvaluation = category_result.final_output
+
+            # 提取得分
+            motivation_score = motivation_eval.动机维度得分
+            category_score = category_eval.品类维度得分
+
+            # 应用新规则计算最终得分
+            final_score, rule_applied = calculate_final_score_v2(
+                motivation_score, category_score
+            )
+
+            # 组合评估理由
+            core_motivation = motivation_eval.原始问题核心动机提取.简要说明核心动机
+            motivation_reason = motivation_eval.简要说明动机维度相关度理由
+            category_reason = category_eval.简要说明品类维度相关度理由
+
+            combined_reason = (
+                f'【评估对象】词条"{text}" vs 作用域词条"{scope_text}"\n'
+                f"【核心动机】{core_motivation}\n"
+                f"【动机维度 {motivation_score:.2f}】{motivation_reason}\n"
+                f"【品类维度 {category_score:.2f}】{category_reason}\n"
+                f"【最终得分 {final_score:.2f}】"
+            )
+
+            # 添加规则说明
+            if rule_applied:
+                combined_reason += f"\n【规则说明】{rule_applied}"
+
+            # 存入缓存
+            if cache is not None:
+                cache[cache_key] = (final_score, combined_reason)
+
+            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)
+            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
+
+
+# ============================================================================
+# v125 新增辅助函数(用于新评分逻辑)
+# ============================================================================
+
+def get_source_word_score(
+    word_text: str,
+    segment: Segment,
+    context: RunContext
+) -> float:
+    """
+    查找来源词的得分
+
+    查找顺序:
+    1. 先查 segment.word_scores (Round 0的单个词)
+    2. 再查 context.word_score_history (Round 1+的组合)
+
+    Args:
+        word_text: 词文本
+        segment: 该词所在的segment
+        context: 运行上下文
+
+    Returns:
+        词的得分,找不到返回0.0
+    """
+    # 优先查Round 0的词得分
+    if word_text in segment.word_scores:
+        return segment.word_scores[word_text]
+
+    # 其次查历史组合得分
+    if word_text in context.word_score_history:
+        return context.word_score_history[word_text]
+
+    # 都找不到
+    print(f"  ⚠️  警告: 未找到来源词得分: {word_text}")
+    return 0.0
+
+
+async def evaluate_domain_combination_round1(
+    comb: DomainCombination,
+    segments: list[Segment],
+    context: RunContext
+) -> tuple[float, str]:
+    """
+    Round 1 域内组合评估(新逻辑)
+
+    最终得分 = 品类得分 × 原始域得分
+
+    Args:
+        comb: 域内组合对象
+        segments: 所有segment列表
+        context: 运行上下文
+
+    Returns:
+        (最终得分, 评估理由)
+    """
+    # 获取所属segment
+    domain_idx = comb.domains[0] if comb.domains else 0
+    segment = segments[domain_idx] if 0 <= domain_idx < len(segments) else None
+
+    if not segment:
+        return 0.0, "错误: 无法找到所属segment"
+
+    # 拼接作用域文本
+    scope_text = segment.text
+
+    # 准备输入
+    eval_input = f"""
+<同一作用域词条>
+{scope_text}
+</同一作用域词条>
+
+<词条>
+{comb.text}
+</词条>
+
+请评估词条与同一作用域词条的匹配度。
+"""
+
+    # 只调用品类评估器
+    try:
+        category_result = await Runner.run(scope_category_evaluator, eval_input)
+        category_eval: CategoryEvaluation = category_result.final_output
+        category_score = category_eval.品类维度得分
+        category_reason = category_eval.简要说明品类维度相关度理由
+    except Exception as e:
+        print(f"  ❌ Round 1品类评估失败: {e}")
+        return 0.0, f"评估失败: {str(e)[:100]}"
+
+    # 计算最终得分
+    domain_score = segment.score_with_o
+    final_score = category_score * domain_score
+
+    # 组合评估理由
+    combined_reason = (
+        f'【Round 1 域内评估】\n'
+        f'【评估对象】组合"{comb.text}" vs 作用域"{scope_text}"\n'
+        f'【品类得分】{category_score:.2f} - {category_reason}\n'
+        f'【原始域得分】{domain_score:.2f}\n'
+        f'【计算公式】品类得分 × 域得分 = {category_score:.2f} × {domain_score:.2f}\n'
+        f'【最终得分】{final_score:.2f}'
+    )
+
+    return final_score, combined_reason
+
+
+async def evaluate_domain_combination_round2plus(
+    comb: DomainCombination,
+    segments: list[Segment],
+    context: RunContext
+) -> tuple[float, str]:
+    """
+    Round 2+ 域间组合评估(新逻辑 - 两步评估相乘)
+
+    步骤:
+    1. 计算全域组合得分 A: 全域组合 vs 原始query(动机+品类两维)
+    2. 计算部分组合得分 B: 部分组合 vs 全域组合(域内评估)
+    3. 最终得分 = A × B,截断到1.0
+
+    Args:
+        comb: 域间组合对象
+        segments: 所有segment列表
+        context: 运行上下文
+
+    Returns:
+        (最终得分, 评估理由)
+    """
+    # 全域组合文本 = 拼接所有参与组合的segments
+    full_domain_text = "".join(comb.from_segments)
+
+    # 步骤1: 计算全域组合得分 A
+    # 全域组合 vs 原始问题(动机+品类两维评估)
+    score_A, reason_A = await evaluate_with_o_round0(
+        full_domain_text,
+        context.o,  # 原始问题
+        context.evaluation_cache
+    )
+
+    # 步骤2: 计算部分组合得分 B
+    # 部分组合 vs 全域组合(域内评估)
+    # 🆕 手动评估,以支持动机得分为0时的特殊权重规则
+
+    # 检查缓存
+    cache_key = f"scope:{comb.text}:{full_domain_text}"
+    if cache_key in context.evaluation_cache:
+        score_B, reason_B = context.evaluation_cache[cache_key]
+        print(f"  ⚡ 域内缓存命中: {comb.text} -> {score_B:.2f}")
+    else:
+        # 准备评估输入
+        eval_input_step2 = f"""
+<同一作用域词条>
+{full_domain_text}
+</同一作用域词条>
+
+<词条>
+{comb.text}
+</词条>
+
+请评估词条与同一作用域词条的匹配度。
+"""
+
+        # 并发调用动机和品类评估器
+        motivation_task = Runner.run(scope_motivation_evaluator, eval_input_step2)
+        category_task = Runner.run(scope_category_evaluator, eval_input_step2)
+
+        motivation_result, category_result = await asyncio.gather(
+            motivation_task,
+            category_task
+        )
+
+        # 获取评估结果
+        motivation_eval: MotivationEvaluation = motivation_result.final_output
+        category_eval: CategoryEvaluation = category_result.final_output
+
+        # 提取得分
+        motivation_score_B = motivation_eval.动机维度得分
+        category_score_B = category_eval.品类维度得分
+
+        # 🆕 根据动机得分调整权重
+        if motivation_score_B == 0:
+            # 动机为0,只使用品类得分
+            score_B = category_score_B
+            weight_rule = "动机得分=0,权重调整为:品类100%"
+        else:
+            # 动机不为0,使用标准规则
+            score_B, rule_applied = calculate_final_score_v2(
+                motivation_score_B, category_score_B
+            )
+            weight_rule = rule_applied if rule_applied else "标准权重:动机70% + 品类30%"
+
+        # 组合评估理由
+        core_motivation = motivation_eval.原始问题核心动机提取.简要说明核心动机
+        motivation_reason = motivation_eval.简要说明动机维度相关度理由
+        category_reason = category_eval.简要说明品类维度相关度理由
+
+        reason_B = (
+            f'【评估对象】词条"{comb.text}" vs 作用域词条"{full_domain_text}"\n'
+            f"【核心动机】{core_motivation}\n"
+            f"【动机维度 {motivation_score_B:.2f}】{motivation_reason}\n"
+            f"【品类维度 {category_score_B:.2f}】{category_reason}\n"
+            f"【权重规则】{weight_rule}\n"
+            f"【最终得分 {score_B:.2f}】"
+        )
+
+        # 缓存结果
+        context.evaluation_cache[cache_key] = (score_B, reason_B)
+
+    # 步骤3: 计算最终得分
+    final_score = score_A * score_B
+    final_score = min(1.0, max(-1.0, final_score))  # 截断到[-1.0, 1.0]
+
+    # 组合评估理由
+    combined_reason = (
+        f'【Round 2+ 域间评估(两步评估相乘)】\n'
+        f'【评估对象】部分组合 "{comb.text}"\n'
+        f'\n'
+        f'【步骤1: 全域组合得分 A】\n'
+        f'  全域组合文本: "{full_domain_text}"\n'
+        f'  评估方式: 全域组合 vs 原始问题(动机+品类两维)\n'
+        f'  {reason_A}\n'
+        f'  得分A = {score_A:.2f}\n'
+        f'\n'
+        f'【步骤2: 部分组合得分 B】\n'
+        f'  部分组合文本: "{comb.text}"\n'
+        f'  评估方式: 部分组合 vs 全域组合(域内评估)\n'
+        f'  {reason_B}\n'
+        f'  得分B = {score_B:.2f}\n'
+        f'\n'
+        f'【最终得分】A × B = {score_A:.2f} × {score_B:.2f} = {score_A * score_B:.2f}\n'
+        f'【截断后】{final_score:.2f}'
+    )
+
+    return final_score, combined_reason
+
+
+# ============================================================================
+# 核心流程函数
+# ============================================================================
+
+async def initialize(o: str, context: RunContext) -> tuple[list[Seg], list[Word], list[Q], list[Seed]]:
+    """
+    初始化阶段
+
+    Returns:
+        (seg_list, word_list_1, q_list_1, seed_list)
+    """
+    print(f"\n{'='*60}")
+    print(f"初始化阶段")
+    print(f"{'='*60}")
+
+    # 1. 分词:原始问题(o) ->分词-> seg_list
+    print(f"\n[步骤1] 分词...")
+    result = await Runner.run(word_segmenter, o)
+    segmentation: WordSegmentation = result.final_output
+
+    seg_list = []
+    for word in segmentation.words:
+        seg_list.append(Seg(text=word, from_o=o))
+
+    print(f"分词结果: {[s.text for s in seg_list]}")
+    print(f"分词理由: {segmentation.reasoning}")
+
+    # 2. 分词评估:seg_list -> 每个seg与o进行评分(使用信号量限制并发数)
+    print(f"\n[步骤2] 评估每个分词与原始问题的相关度...")
+
+    MAX_CONCURRENT_SEG_EVALUATIONS = 10
+    seg_semaphore = asyncio.Semaphore(MAX_CONCURRENT_SEG_EVALUATIONS)
+
+    async def evaluate_seg(seg: Seg) -> Seg:
+        async with seg_semaphore:
+            # 初始化阶段的分词评估使用第一轮 prompt (round_num=1)
+            seg.score_with_o, seg.reason = await evaluate_with_o(seg.text, o, context.evaluation_cache, round_num=1)
+            return seg
+
+    if seg_list:
+        print(f"  开始评估 {len(seg_list)} 个分词(并发限制: {MAX_CONCURRENT_SEG_EVALUATIONS})...")
+        eval_tasks = [evaluate_seg(seg) for seg in seg_list]
+        await asyncio.gather(*eval_tasks)
+
+    for seg in seg_list:
+        print(f"  {seg.text}: {seg.score_with_o:.2f}")
+
+    # 3. 构建word_list_1: seg_list -> word_list_1(固定词库)
+    print(f"\n[步骤3] 构建word_list_1(固定词库)...")
+    word_list_1 = []
+    for seg in seg_list:
+        word_list_1.append(Word(
+            text=seg.text,
+            score_with_o=seg.score_with_o,
+            from_o=o
+        ))
+    print(f"word_list_1(固定): {[w.text for w in word_list_1]}")
+
+    # 4. 构建q_list_1:seg_list 作为 q_list_1
+    print(f"\n[步骤4] 构建q_list_1...")
+    q_list_1 = []
+    for seg in seg_list:
+        q_list_1.append(Q(
+            text=seg.text,
+            score_with_o=seg.score_with_o,
+            reason=seg.reason,
+            from_source="seg"
+        ))
+    print(f"q_list_1: {[q.text for q in q_list_1]}")
+
+    # 5. 构建seed_list: seg_list -> seed_list
+    print(f"\n[步骤5] 构建seed_list...")
+    seed_list = []
+    for seg in seg_list:
+        seed_list.append(Seed(
+            text=seg.text,
+            added_words=[],
+            from_type="seg",
+            score_with_o=seg.score_with_o
+        ))
+    print(f"seed_list: {[s.text for s in seed_list]}")
+
+    return seg_list, word_list_1, q_list_1, seed_list
+
+
+async def run_round(
+    round_num: int,
+    q_list: list[Q],
+    word_list_1: list[Word],
+    seed_list: list[Seed],
+    o: str,
+    context: RunContext,
+    xiaohongshu_api: XiaohongshuSearchRecommendations,
+    xiaohongshu_search: XiaohongshuSearch,
+    sug_threshold: float = 0.7
+) -> tuple[list[Q], list[Seed], list[Search]]:
+    """
+    运行一轮
+
+    Args:
+        round_num: 轮次编号
+        q_list: 当前轮的q列表
+        word_list_1: 固定的词库(第0轮分词结果)
+        seed_list: 当前的seed列表
+        o: 原始问题
+        context: 运行上下文
+        xiaohongshu_api: 建议词API
+        xiaohongshu_search: 搜索API
+        sug_threshold: suggestion的阈值
+
+    Returns:
+        (q_list_next, seed_list_next, search_list)
+    """
+    print(f"\n{'='*60}")
+    print(f"第{round_num}轮")
+    print(f"{'='*60}")
+
+    round_data = {
+        "round_num": round_num,
+        "input_q_list": [{"text": q.text, "score": q.score_with_o, "type": "query"} for q in q_list],
+        "input_word_list_1_size": len(word_list_1),
+        "input_seed_list_size": len(seed_list)
+    }
+
+    # 1. 请求sug:q_list -> 每个q请求sug接口 -> sug_list_list
+    print(f"\n[步骤1] 为每个q请求建议词...")
+    sug_list_list = []  # list of list
+    for q in q_list:
+        print(f"\n  处理q: {q.text}")
+        suggestions = xiaohongshu_api.get_recommendations(keyword=q.text)
+
+        q_sug_list = []
+        if suggestions:
+            print(f"    获取到 {len(suggestions)} 个建议词")
+            for sug_text in suggestions:
+                sug = Sug(
+                    text=sug_text,
+                    from_q=QFromQ(text=q.text, score_with_o=q.score_with_o)
+                )
+                q_sug_list.append(sug)
+        else:
+            print(f"    未获取到建议词")
+
+        sug_list_list.append(q_sug_list)
+
+    # 2. sug评估:sug_list_list -> 每个sug与o进行评分(并发)
+    print(f"\n[步骤2] 评估每个建议词与原始问题的相关度...")
+
+    # 2.1 收集所有需要评估的sug,并记录它们所属的q
+    all_sugs = []
+    sug_to_q_map = {}  # 记录每个sug属于哪个q
+    for i, q_sug_list in enumerate(sug_list_list):
+        if q_sug_list:
+            q_text = q_list[i].text
+            for sug in q_sug_list:
+                all_sugs.append(sug)
+                sug_to_q_map[id(sug)] = q_text
+
+    # 2.2 并发评估所有sug(使用信号量限制并发数)
+    # 每个 evaluate_sug 内部会并发调用 2 个 LLM,所以这里限制为 5,实际并发 LLM 请求为 10
+    MAX_CONCURRENT_EVALUATIONS = 5
+    semaphore = asyncio.Semaphore(MAX_CONCURRENT_EVALUATIONS)
+
+    async def evaluate_sug(sug: Sug) -> Sug:
+        async with semaphore:  # 限制并发数
+            # 根据轮次选择 prompt: 第一轮使用 round1 prompt,后续使用标准 prompt
+            sug.score_with_o, sug.reason = await evaluate_with_o(sug.text, o, context.evaluation_cache, round_num=round_num)
+            return sug
+
+    if all_sugs:
+        print(f"  开始评估 {len(all_sugs)} 个建议词(并发限制: {MAX_CONCURRENT_EVALUATIONS})...")
+        eval_tasks = [evaluate_sug(sug) for sug in all_sugs]
+        await asyncio.gather(*eval_tasks)
+
+    # 2.3 打印结果并组织到sug_details
+    sug_details = {}  # 保存每个Q对应的sug列表
+    for i, q_sug_list in enumerate(sug_list_list):
+        if q_sug_list:
+            q_text = q_list[i].text
+            print(f"\n  来自q '{q_text}' 的建议词:")
+            sug_details[q_text] = []
+            for sug in q_sug_list:
+                print(f"    {sug.text}: {sug.score_with_o:.2f}")
+                # 保存到sug_details
+                sug_details[q_text].append({
+                    "text": sug.text,
+                    "score": sug.score_with_o,
+                    "reason": sug.reason,
+                    "type": "sug"
+                })
+
+    # 2.4 剪枝判断(已禁用 - 保留所有分支)
+    pruned_query_texts = set()
+    if False:  # 原: if round_num >= 2:  # 剪枝功能已禁用,保留代码以便后续调整
+        print(f"\n[剪枝判断] 第{round_num}轮开始应用剪枝策略...")
+        for i, q in enumerate(q_list):
+            q_sug_list = sug_list_list[i]
+
+            if len(q_sug_list) == 0:
+                continue  # 没有sug则不剪枝
+
+            # 剪枝条件1: 所有sug分数都低于query分数
+            all_lower_than_query = all(sug.score_with_o < q.score_with_o for sug in q_sug_list)
+            # 剪枝条件2: 所有sug分数都低于0.5
+            all_below_threshold = all(sug.score_with_o < 0.5 for sug in q_sug_list)
+
+            if all_lower_than_query and all_below_threshold:
+                pruned_query_texts.add(q.text)
+                max_sug_score = max(sug.score_with_o for sug in q_sug_list)
+                print(f"  🔪 剪枝: {q.text} (query分数:{q.score_with_o:.2f}, sug最高分:{max_sug_score:.2f}, 全部<0.5)")
+
+        if pruned_query_texts:
+            print(f"  本轮共剪枝 {len(pruned_query_texts)} 个query")
+        else:
+            print(f"  本轮无query被剪枝")
+    else:
+        print(f"\n[剪枝判断] 剪枝功能已禁用,保留所有分支")
+
+    # 3. search_list构建
+    print(f"\n[步骤3] 构建search_list(阈值>{sug_threshold})...")
+    search_list = []
+    high_score_sugs = [sug for sug in all_sugs if sug.score_with_o > sug_threshold]
+
+    if high_score_sugs:
+        print(f"  找到 {len(high_score_sugs)} 个高分建议词")
+
+        # 并发搜索
+        async def search_for_sug(sug: Sug) -> Search:
+            print(f"    搜索: {sug.text}")
+            try:
+                search_result = xiaohongshu_search.search(keyword=sug.text)
+                result_str = search_result.get("result", "{}")
+                if isinstance(result_str, str):
+                    result_data = json.loads(result_str)
+                else:
+                    result_data = result_str
+
+                notes = result_data.get("data", {}).get("data", [])
+                post_list = []
+                for note in notes[:10]:  # 只取前10个
+                    post = process_note_data(note)
+                    post_list.append(post)
+
+                print(f"      → 找到 {len(post_list)} 个帖子")
+
+                return Search(
+                    text=sug.text,
+                    score_with_o=sug.score_with_o,
+                    from_q=sug.from_q,
+                    post_list=post_list
+                )
+            except Exception as e:
+                print(f"      ✗ 搜索失败: {e}")
+                return Search(
+                    text=sug.text,
+                    score_with_o=sug.score_with_o,
+                    from_q=sug.from_q,
+                    post_list=[]
+                )
+
+        search_tasks = [search_for_sug(sug) for sug in high_score_sugs]
+        search_list = await asyncio.gather(*search_tasks)
+    else:
+        print(f"  没有高分建议词,search_list为空")
+
+    # 4. 构建q_list_next
+    print(f"\n[步骤4] 构建q_list_next...")
+    q_list_next = []
+    existing_q_texts = set()  # 用于去重
+    add_word_details = {}  # 保存每个seed对应的组合词列表
+    all_seed_combinations = []  # 保存本轮所有seed的组合词(用于后续构建seed_list_next)
+
+    # 4.1 对于seed_list中的每个seed,从word_list_1中选词组合,产生Top 5
+    print(f"\n  4.1 为每个seed加词(产生Top 5组合)...")
+    for seed in seed_list:
+        print(f"\n    处理seed: {seed.text}")
+
+        # 剪枝检查:跳过被剪枝的seed
+        if seed.text in pruned_query_texts:
+            print(f"      ⊗ 跳过被剪枝的seed: {seed.text}")
+            continue
+
+        # 从固定词库word_list_1筛选候选词
+        candidate_words = []
+        for word in word_list_1:
+            # 检查词是否已在seed中
+            if word.text in seed.text:
+                continue
+            # 检查词是否已被添加过
+            if word.text in seed.added_words:
+                continue
+            candidate_words.append(word)
+
+        if not candidate_words:
+            print(f"      没有可用的候选词")
+            continue
+
+        print(f"      候选词数量: {len(candidate_words)}")
+
+        # 调用Agent一次性选择并组合Top 5(添加重试机制)
+        candidate_words_text = ', '.join([w.text for w in candidate_words])
+        selection_input = f"""
+<原始问题>
+{o}
+</原始问题>
+
+<当前Seed>
+{seed.text}
+</当前Seed>
+
+<候选词列表>
+{candidate_words_text}
+</候选词列表>
+
+请从候选词列表中选择最多5个最合适的词,分别与当前seed组合成新的query。
+"""
+
+        # 重试机制
+        max_retries = 2
+        selection_result = None
+        for attempt in range(max_retries):
+            try:
+                result = await Runner.run(word_selector, selection_input)
+                selection_result = result.final_output
+                break  # 成功则跳出
+            except Exception as e:
+                error_msg = str(e)
+                if attempt < max_retries - 1:
+                    print(f"      ⚠️  选词失败 (尝试 {attempt+1}/{max_retries}): {error_msg[:100]}")
+                    await asyncio.sleep(1)
+                else:
+                    print(f"      ❌ 选词失败,跳过该seed: {error_msg[:100]}")
+                    break
+
+        if selection_result is None:
+            print(f"      跳过seed: {seed.text}")
+            continue
+
+        print(f"      Agent选择了 {len(selection_result.combinations)} 个组合")
+        print(f"      整体选择思路: {selection_result.overall_reasoning}")
+
+        # 并发评估所有组合的相关度
+        async def evaluate_combination(comb: WordCombination) -> dict:
+            combined = comb.combined_query
+
+            # 验证:组合结果必须包含完整的seed和word
+            # 检查是否包含seed的所有字符
+            seed_chars_in_combined = all(char in combined for char in seed.text)
+            # 检查是否包含word的所有字符
+            word_chars_in_combined = all(char in combined for char in comb.selected_word)
+
+            if not seed_chars_in_combined or not word_chars_in_combined:
+                print(f"        ⚠️  警告:组合不完整")
+                print(f"          Seed: {seed.text}")
+                print(f"          Word: {comb.selected_word}")
+                print(f"          组合: {combined}")
+                print(f"          包含完整seed? {seed_chars_in_combined}")
+                print(f"          包含完整word? {word_chars_in_combined}")
+                # 返回极低分数,让这个组合不会被选中
+                return {
+                    'word': comb.selected_word,
+                    'query': combined,
+                    'score': -1.0,  # 极低分数
+                    'reason': f"组合不完整:缺少seed或word的部分内容",
+                    'reasoning': comb.reasoning
+                }
+
+            # 正常评估,根据轮次选择 prompt
+            score, reason = await evaluate_with_o(combined, o, context.evaluation_cache, round_num=round_num)
+            return {
+                'word': comb.selected_word,
+                'query': combined,
+                'score': score,
+                'reason': reason,
+                'reasoning': comb.reasoning
+            }
+
+        eval_tasks = [evaluate_combination(comb) for comb in selection_result.combinations]
+        top_5 = await asyncio.gather(*eval_tasks)
+
+        print(f"      评估完成,得到 {len(top_5)} 个组合")
+
+        # 将Top 5全部加入q_list_next(去重检查 + 得分过滤)
+        for comb in top_5:
+            # 得分过滤:组合词必须比种子提升至少REQUIRED_SCORE_GAIN才能加入下一轮
+            if comb['score'] < seed.score_with_o + REQUIRED_SCORE_GAIN:
+                print(f"        ⊗ 跳过低分: {comb['query']} (分数{comb['score']:.2f} < 种子{seed.score_with_o:.2f} + {REQUIRED_SCORE_GAIN:.2f})")
+                continue
+
+            # 去重检查
+            if comb['query'] in existing_q_texts:
+                print(f"        ⊗ 跳过重复: {comb['query']}")
+                continue
+
+            print(f"        ✓ {comb['query']} (分数: {comb['score']:.2f} > 种子: {seed.score_with_o:.2f})")
+
+            new_q = Q(
+                text=comb['query'],
+                score_with_o=comb['score'],
+                reason=comb['reason'],
+                from_source="add"
+            )
+            q_list_next.append(new_q)
+            existing_q_texts.add(comb['query'])  # 记录到去重集合
+
+            # 记录已添加的词
+            seed.added_words.append(comb['word'])
+
+        # 保存到add_word_details
+        add_word_details[seed.text] = [
+            {
+                "text": comb['query'],
+                "score": comb['score'],
+                "reason": comb['reason'],
+                "selected_word": comb['word'],
+                "seed_score": seed.score_with_o,  # 添加原始种子的得分
+                "type": "add"
+            }
+            for comb in top_5
+        ]
+
+        # 保存到all_seed_combinations(用于构建seed_list_next)
+        # 附加seed_score,用于后续过滤
+        for comb in top_5:
+            comb['seed_score'] = seed.score_with_o
+        all_seed_combinations.extend(top_5)
+
+    # 4.2 对于sug_list_list中,每个sug大于来自的query分数,加到q_list_next(去重检查)
+    print(f"\n  4.2 将高分sug加入q_list_next...")
+    for sug in all_sugs:
+        # 剪枝检查:跳过来自被剪枝query的sug
+        if sug.from_q and sug.from_q.text in pruned_query_texts:
+            print(f"    ⊗ 跳过来自被剪枝query的sug: {sug.text} (来源: {sug.from_q.text})")
+            continue
+
+        # sug必须比来源query提升至少REQUIRED_SCORE_GAIN才能加入下一轮
+        if sug.from_q and sug.score_with_o >= sug.from_q.score_with_o + REQUIRED_SCORE_GAIN:
+            # 去重检查
+            if sug.text in existing_q_texts:
+                print(f"    ⊗ 跳过重复: {sug.text}")
+                continue
+
+            new_q = Q(
+                text=sug.text,
+                score_with_o=sug.score_with_o,
+                reason=sug.reason,
+                from_source="sug"
+            )
+            q_list_next.append(new_q)
+            existing_q_texts.add(sug.text)  # 记录到去重集合
+            print(f"    ✓ {sug.text} (分数: {sug.score_with_o:.2f} >= 来源query: {sug.from_q.score_with_o:.2f} + {REQUIRED_SCORE_GAIN:.2f})")
+
+    # 5. 构建seed_list_next(关键修改:不保留上一轮的seed)
+    print(f"\n[步骤5] 构建seed_list_next(不保留上轮seed)...")
+    seed_list_next = []
+    existing_seed_texts = set()
+
+    # 5.1 加入本轮所有组合词(只加入得分提升的)
+    print(f"  5.1 加入本轮所有组合词(得分过滤)...")
+    for comb in all_seed_combinations:
+        # 得分过滤:组合词必须比种子提升至少REQUIRED_SCORE_GAIN才作为下一轮种子
+        seed_score = comb.get('seed_score', 0)
+        if comb['score'] < seed_score + REQUIRED_SCORE_GAIN:
+            print(f"    ⊗ 跳过低分: {comb['query']} (分数{comb['score']:.2f} < 种子{seed_score:.2f} + {REQUIRED_SCORE_GAIN:.2f})")
+            continue
+
+        if comb['query'] not in existing_seed_texts:
+            new_seed = Seed(
+                text=comb['query'],
+                added_words=[],  # 新seed的added_words清空
+                from_type="add",
+                score_with_o=comb['score']
+            )
+            seed_list_next.append(new_seed)
+            existing_seed_texts.add(comb['query'])
+            print(f"    ✓ {comb['query']} (分数: {comb['score']:.2f} >= 种子: {seed_score:.2f} + {REQUIRED_SCORE_GAIN:.2f})")
+
+    # 5.2 加入高分sug
+    print(f"  5.2 加入高分sug...")
+    for sug in all_sugs:
+        # 剪枝检查:跳过来自被剪枝query的sug
+        if sug.from_q and sug.from_q.text in pruned_query_texts:
+            continue
+
+        # sug必须比来源query提升至少REQUIRED_SCORE_GAIN才作为下一轮种子
+        if sug.from_q and sug.score_with_o >= sug.from_q.score_with_o + REQUIRED_SCORE_GAIN and sug.text not in existing_seed_texts:
+            new_seed = Seed(
+                text=sug.text,
+                added_words=[],
+                from_type="sug",
+                score_with_o=sug.score_with_o
+            )
+            seed_list_next.append(new_seed)
+            existing_seed_texts.add(sug.text)
+            print(f"    ✓ {sug.text} (分数: {sug.score_with_o:.2f} >= 来源query: {sug.from_q.score_with_o:.2f} + {REQUIRED_SCORE_GAIN:.2f})")
+
+    # 序列化搜索结果数据(包含帖子详情)
+    search_results_data = []
+    for search in search_list:
+        search_results_data.append({
+            "text": search.text,
+            "score_with_o": search.score_with_o,
+            "post_list": [
+                {
+                    "note_id": post.note_id,
+                    "note_url": post.note_url,
+                    "title": post.title,
+                    "body_text": post.body_text,
+                    "images": post.images,
+                    "interact_info": post.interact_info
+                }
+                for post in search.post_list
+            ]
+        })
+
+    # 记录本轮数据
+    round_data.update({
+        "sug_count": len(all_sugs),
+        "high_score_sug_count": len(high_score_sugs),
+        "search_count": len(search_list),
+        "total_posts": sum(len(s.post_list) for s in search_list),
+        "q_list_next_size": len(q_list_next),
+        "seed_list_next_size": len(seed_list_next),
+        "total_combinations": len(all_seed_combinations),
+        "pruned_query_count": len(pruned_query_texts),
+        "pruned_queries": list(pruned_query_texts),
+        "output_q_list": [{"text": q.text, "score": q.score_with_o, "reason": q.reason, "from": q.from_source, "type": "query"} for q in q_list_next],
+        "seed_list_next": [{"text": seed.text, "from": seed.from_type, "score": seed.score_with_o} for seed in seed_list_next],
+        "sug_details": sug_details,
+        "add_word_details": add_word_details,
+        "search_results": search_results_data
+    })
+    context.rounds.append(round_data)
+
+    print(f"\n本轮总结:")
+    print(f"  建议词数量: {len(all_sugs)}")
+    print(f"  高分建议词: {len(high_score_sugs)}")
+    print(f"  搜索数量: {len(search_list)}")
+    print(f"  帖子总数: {sum(len(s.post_list) for s in search_list)}")
+    print(f"  组合词数量: {len(all_seed_combinations)}")
+    print(f"  下轮q数量: {len(q_list_next)}")
+    print(f"  下轮seed数量: {len(seed_list_next)}")
+
+    return q_list_next, seed_list_next, search_list
+
+
+async def iterative_loop(
+    context: RunContext,
+    max_rounds: int = 2,
+    sug_threshold: float = 0.7
+):
+    """主迭代循环"""
+
+    print(f"\n{'='*60}")
+    print(f"开始迭代循环")
+    print(f"最大轮数: {max_rounds}")
+    print(f"sug阈值: {sug_threshold}")
+    print(f"{'='*60}")
+
+    # 初始化
+    seg_list, word_list_1, q_list, seed_list = await initialize(context.o, context)
+
+    # API实例
+    xiaohongshu_api = XiaohongshuSearchRecommendations()
+    xiaohongshu_search = XiaohongshuSearch()
+
+    # 保存初始化数据
+    context.rounds.append({
+        "round_num": 0,
+        "type": "initialization",
+        "seg_list": [{"text": s.text, "score": s.score_with_o, "reason": s.reason, "type": "seg"} for s in seg_list],
+        "word_list_1": [{"text": w.text, "score": w.score_with_o} for w in word_list_1],
+        "q_list_1": [{"text": q.text, "score": q.score_with_o, "reason": q.reason, "type": "query"} for q in q_list],
+        "seed_list": [{"text": s.text, "from_type": s.from_type, "score": s.score_with_o, "type": "seed"} for s in seed_list]
+    })
+
+    # 收集所有搜索结果
+    all_search_list = []
+
+    # 迭代
+    round_num = 1
+    while q_list and round_num <= max_rounds:
+        q_list, seed_list, search_list = await run_round(
+            round_num=round_num,
+            q_list=q_list,
+            word_list_1=word_list_1,  # 传递固定词库
+            seed_list=seed_list,
+            o=context.o,
+            context=context,
+            xiaohongshu_api=xiaohongshu_api,
+            xiaohongshu_search=xiaohongshu_search,
+            sug_threshold=sug_threshold
+        )
+
+        all_search_list.extend(search_list)
+        round_num += 1
+
+    print(f"\n{'='*60}")
+    print(f"迭代完成")
+    print(f"  总轮数: {round_num - 1}")
+    print(f"  总搜索次数: {len(all_search_list)}")
+    print(f"  总帖子数: {sum(len(s.post_list) for s in all_search_list)}")
+    print(f"{'='*60}")
+
+    return all_search_list
+
+
+# ============================================================================
+# v121 新架构核心流程函数
+# ============================================================================
+
+async def initialize_v2(o: str, context: RunContext) -> list[Segment]:
+    """
+    v121 Round 0 初始化阶段
+
+    流程:
+    1. 语义分段: 调用 semantic_segmenter 将原始问题拆分成语义片段
+    2. 拆词: 对每个segment调用 word_segmenter 进行拆词
+    3. 评估: 对每个segment和词进行评估
+    4. 不进行组合(Round 0只分段和拆词)
+
+    Returns:
+        语义片段列表 (Segment)
+    """
+    print(f"\n{'='*60}")
+    print(f"Round 0: 初始化阶段(语义分段 + 拆词)")
+    print(f"{'='*60}")
+
+    # 1. 语义分段
+    print(f"\n[步骤1] 语义分段...")
+    result = await Runner.run(semantic_segmenter, o)
+    segmentation: SemanticSegmentation = result.final_output
+
+    print(f"语义分段结果: {len(segmentation.segments)} 个片段")
+    print(f"整体分段思路: {segmentation.overall_reasoning}")
+
+    segment_list = []
+    for seg_item in segmentation.segments:
+        segment = Segment(
+            text=seg_item.segment_text,
+            type=seg_item.segment_type,
+            from_o=o
+        )
+        segment_list.append(segment)
+        print(f"  - [{segment.type}] {segment.text}")
+
+    # 2. 对每个segment拆词并评估
+    print(f"\n[步骤2] 对每个segment拆词并评估...")
+
+    MAX_CONCURRENT_EVALUATIONS = 5
+    semaphore = asyncio.Semaphore(MAX_CONCURRENT_EVALUATIONS)
+
+    async def process_segment(segment: Segment) -> Segment:
+        """处理单个segment: 拆词 + 评估segment + 评估词"""
+        async with semaphore:
+            # 2.1 拆词
+            word_result = await Runner.run(word_segmenter, segment.text)
+            word_segmentation: WordSegmentation = word_result.final_output
+            segment.words = word_segmentation.words
+
+            # 2.2 评估segment与原始问题的相关度(使用Round 0专用评估)
+            segment.score_with_o, segment.reason = await evaluate_with_o_round0(
+                segment.text, o, context.evaluation_cache
+            )
+
+            # 2.3 评估每个词与原始问题的相关度(使用Round 0专用评估)
+            word_eval_tasks = []
+            for word in segment.words:
+                async def eval_word(w: str) -> tuple[str, float, str]:
+                    score, reason = await evaluate_with_o_round0(w, o, context.evaluation_cache)
+                    return w, score, reason
+                word_eval_tasks.append(eval_word(word))
+
+            word_results = await asyncio.gather(*word_eval_tasks)
+            for word, score, reason in word_results:
+                segment.word_scores[word] = score
+                segment.word_reasons[word] = reason
+
+            return segment
+
+    if segment_list:
+        print(f"  开始处理 {len(segment_list)} 个segment(并发限制: {MAX_CONCURRENT_EVALUATIONS})...")
+        process_tasks = [process_segment(seg) for seg in segment_list]
+        await asyncio.gather(*process_tasks)
+
+    # 打印步骤1结果
+    print(f"\n[步骤1: 分段及拆词 结果]")
+    for segment in segment_list:
+        print(f"  [{segment.type}] {segment.text} (分数: {segment.score_with_o:.2f})")
+        print(f"    拆词: {segment.words}")
+        for word in segment.words:
+            score = segment.word_scores.get(word, 0.0)
+            print(f"      - {word}: {score:.2f}")
+
+    # 保存到context(保留旧格式以兼容)
+    context.segments = [
+        {
+            "text": seg.text,
+            "type": seg.type,
+            "score": seg.score_with_o,
+            "reason": seg.reason,
+            "words": seg.words,
+            "word_scores": seg.word_scores,
+            "word_reasons": seg.word_reasons
+        }
+        for seg in segment_list
+    ]
+
+    # 保存 Round 0 到 context.rounds(新格式用于可视化)
+    context.rounds.append({
+        "round_num": 0,
+        "type": "initialization",
+        "segments": [
+            {
+                "text": seg.text,
+                "type": seg.type,
+                "domain_index": idx,
+                "score": seg.score_with_o,
+                "reason": seg.reason,
+                "words": [
+                    {
+                        "text": word,
+                        "score": seg.word_scores.get(word, 0.0),
+                        "reason": seg.word_reasons.get(word, "")
+                    }
+                    for word in seg.words
+                ]
+            }
+            for idx, seg in enumerate(segment_list)
+        ]
+    })
+
+    # 🆕 存储Round 0的所有word得分到历史记录
+    print(f"\n[存储Round 0词得分到历史记录]")
+    for segment in segment_list:
+        for word, score in segment.word_scores.items():
+            context.word_score_history[word] = score
+            print(f"  {word}: {score:.2f}")
+
+    print(f"\n[Round 0 完成]")
+    print(f"  分段数: {len(segment_list)}")
+    total_words = sum(len(seg.words) for seg in segment_list)
+    print(f"  总词数: {total_words}")
+
+    return segment_list
+
+
+async def run_round_v2(
+    round_num: int,
+    query_input: list[Q],
+    segments: list[Segment],
+    o: str,
+    context: RunContext,
+    xiaohongshu_api: XiaohongshuSearchRecommendations,
+    xiaohongshu_search: XiaohongshuSearch,
+    sug_threshold: float = 0.7
+) -> tuple[list[Q], list[Search]]:
+    """
+    v121 Round N 执行
+
+    正确的流程顺序:
+    1. 为 query_input 请求SUG
+    2. 评估SUG
+    3. 高分SUG搜索
+    4. N域组合(从segments生成)
+    5. 评估组合
+    6. 生成 q_list_next(组合 + 高分SUG)
+
+    Args:
+        round_num: 轮次编号 (1-4)
+        query_input: 本轮的输入query列表(Round 1是words,Round 2+是上轮输出)
+        segments: 语义片段列表(用于组合)
+        o: 原始问题
+        context: 运行上下文
+        xiaohongshu_api: 建议词API
+        xiaohongshu_search: 搜索API
+        sug_threshold: SUG搜索阈值
+
+    Returns:
+        (q_list_next, search_list)
+    """
+    print(f"\n{'='*60}")
+    print(f"Round {round_num}: {round_num}域组合")
+    print(f"{'='*60}")
+
+    round_data = {
+        "round_num": round_num,
+        "n_domains": round_num,
+        "input_query_count": len(query_input)
+    }
+
+    MAX_CONCURRENT_EVALUATIONS = 5
+    semaphore = asyncio.Semaphore(MAX_CONCURRENT_EVALUATIONS)
+
+    # 步骤1: 为 query_input 请求SUG
+    print(f"\n[步骤1] 为{len(query_input)}个输入query请求SUG...")
+    all_sugs = []
+    sug_details = {}
+
+    for q in query_input:
+        suggestions = xiaohongshu_api.get_recommendations(keyword=q.text)
+        if suggestions:
+            print(f"  {q.text}: 获取到 {len(suggestions)} 个SUG")
+            for sug_text in suggestions:
+                sug = Sug(
+                    text=sug_text,
+                    from_q=QFromQ(text=q.text, score_with_o=q.score_with_o)
+                )
+                all_sugs.append(sug)
+        else:
+            print(f"  {q.text}: 未获取到SUG")
+
+    print(f"  共获取 {len(all_sugs)} 个SUG")
+
+    # 步骤2: 评估SUG
+    if len(all_sugs) > 0:
+        print(f"\n[步骤2] 评估{len(all_sugs)}个SUG...")
+
+        async def evaluate_sug(sug: Sug) -> Sug:
+            async with semaphore:
+                sug.score_with_o, sug.reason = await evaluate_with_o(
+                    sug.text, o, context.evaluation_cache
+                )
+                return sug
+
+        eval_tasks = [evaluate_sug(sug) for sug in all_sugs]
+        await asyncio.gather(*eval_tasks)
+
+        # 打印结果
+        for sug in all_sugs:
+            print(f"    {sug.text}: {sug.score_with_o:.2f}")
+            if sug.from_q:
+                if sug.from_q.text not in sug_details:
+                    sug_details[sug.from_q.text] = []
+                sug_details[sug.from_q.text].append({
+                    "text": sug.text,
+                    "score": sug.score_with_o,
+                    "reason": sug.reason,
+                    "type": "sug"
+                })
+
+    # 步骤3: 搜索高分SUG
+    print(f"\n[步骤3] 搜索高分SUG(阈值 > {sug_threshold})...")
+    high_score_sugs = [sug for sug in all_sugs if sug.score_with_o > sug_threshold]
+    print(f"  找到 {len(high_score_sugs)} 个高分SUG")
+
+    search_list = []
+    if len(high_score_sugs) > 0:
+        async def search_for_sug(sug: Sug) -> Search:
+            print(f"    搜索: {sug.text}")
+            try:
+                search_result = xiaohongshu_search.search(keyword=sug.text)
+                result_str = search_result.get("result", "{}")
+                if isinstance(result_str, str):
+                    result_data = json.loads(result_str)
+                else:
+                    result_data = result_str
+
+                notes = result_data.get("data", {}).get("data", [])
+                post_list = []
+                for note in notes[:10]:
+                    post = process_note_data(note)
+                    post_list.append(post)
+
+                print(f"      → 找到 {len(post_list)} 个帖子")
+
+                return Search(
+                    text=sug.text,
+                    score_with_o=sug.score_with_o,
+                    from_q=sug.from_q,
+                    post_list=post_list
+                )
+            except Exception as e:
+                print(f"      ✗ 搜索失败: {e}")
+                return Search(
+                    text=sug.text,
+                    score_with_o=sug.score_with_o,
+                    from_q=sug.from_q,
+                    post_list=[]
+                )
+
+        search_tasks = [search_for_sug(sug) for sug in high_score_sugs]
+        search_list = await asyncio.gather(*search_tasks)
+
+    # 步骤4: 生成N域组合
+    print(f"\n[步骤4] 生成{round_num}域组合...")
+    domain_combinations = generate_domain_combinations(segments, round_num)
+    print(f"  生成了 {len(domain_combinations)} 个组合")
+
+    if len(domain_combinations) == 0:
+        print(f"  无法生成{round_num}域组合")
+        # 即使无法组合,也返回高分SUG作为下轮输入
+        q_list_next = []
+        for sug in all_sugs:
+            if sug.from_q and sug.score_with_o >= sug.from_q.score_with_o + REQUIRED_SCORE_GAIN:
+                q = Q(
+                    text=sug.text,
+                    score_with_o=sug.score_with_o,
+                    reason=sug.reason,
+                    from_source="sug",
+                    type_label=""
+                )
+                q_list_next.append(q)
+
+        round_data.update({
+            "domain_combinations_count": 0,
+            "sug_count": len(all_sugs),
+            "high_score_sug_count": len(high_score_sugs),
+            "search_count": len(search_list),
+            "sug_details": sug_details,
+            "q_list_next_size": len(q_list_next)
+        })
+        context.rounds.append(round_data)
+        return q_list_next, search_list
+
+    # 步骤5: 评估所有组合
+    print(f"\n[步骤5] 评估{len(domain_combinations)}个组合...")
+
+    async def evaluate_combination(comb: DomainCombination) -> DomainCombination:
+        async with semaphore:
+            # 🆕 根据轮次选择评估逻辑
+            if round_num == 1:
+                # Round 1: 域内评估(新逻辑)
+                comb.score_with_o, comb.reason = await evaluate_domain_combination_round1(
+                    comb, segments, context
+                )
+            else:
+                # Round 2+: 域间评估(新逻辑)
+                comb.score_with_o, comb.reason = await evaluate_domain_combination_round2plus(
+                    comb, segments, context
+                )
+
+            # 🆕 存储组合得分到历史记录
+            context.word_score_history[comb.text] = comb.score_with_o
+
+            return comb
+
+    eval_tasks = [evaluate_combination(comb) for comb in domain_combinations]
+    await asyncio.gather(*eval_tasks)
+
+    # 排序 - 已注释,保持原始顺序
+    # domain_combinations.sort(key=lambda x: x.score_with_o, reverse=True)
+
+    # 打印所有组合(保持原始顺序)
+    evaluation_strategy = 'Round 1 域内评估(品类×域得分)' if round_num == 1 else 'Round 2+ 域间评估(加权系数调整)'
+    print(f"  评估完成,共{len(domain_combinations)}个组合 [策略: {evaluation_strategy}]")
+    for i, comb in enumerate(domain_combinations, 1):
+        print(f"    {i}. {comb.text} {comb.type_label} (分数: {comb.score_with_o:.2f})")
+
+    # 为每个组合补充来源词分数信息,并判断是否超过所有来源词得分
+    for comb in domain_combinations:
+        word_details = []
+        flat_scores: list[float] = []
+        for domain_index, words in zip(comb.domains, comb.source_words):
+            segment = segments[domain_index] if 0 <= domain_index < len(segments) else None
+            segment_type = segment.type if segment else ""
+            segment_text = segment.text if segment else ""
+            items = []
+            for word in words:
+                score = 0.0
+                if segment and word in segment.word_scores:
+                    score = segment.word_scores[word]
+                items.append({
+                    "text": word,
+                    "score": score
+                })
+                flat_scores.append(score)
+            word_details.append({
+                "domain_index": domain_index,
+                "segment_type": segment_type,
+                "segment_text": segment_text,
+                "words": items
+            })
+        comb.source_word_details = word_details
+        comb.source_scores = flat_scores
+        comb.max_source_score = max(flat_scores) if flat_scores else None
+        comb.is_above_source_scores = bool(flat_scores) and all(
+            comb.score_with_o > score for score in flat_scores
+        )
+
+    # 步骤6: 构建 q_list_next(组合 + 高分SUG)
+    print(f"\n[步骤6] 生成下轮输入...")
+    q_list_next: list[Q] = []
+
+    # 6.1 添加高增益SUG(满足增益条件),并按分数排序
+    sug_candidates: list[tuple[Q, Sug]] = []
+    for sug in all_sugs:
+        if sug.from_q and sug.score_with_o >= sug.from_q.score_with_o + REQUIRED_SCORE_GAIN:
+            q = Q(
+                text=sug.text,
+                score_with_o=sug.score_with_o,
+                reason=sug.reason,
+                from_source="sug",
+                type_label=""
+            )
+            sug_candidates.append((q, sug))
+
+    sug_candidates.sort(key=lambda item: item[0].score_with_o, reverse=True)
+    q_list_next.extend([item[0] for item in sug_candidates])
+    high_gain_sugs = [item[1] for item in sug_candidates]
+    print(f"  添加 {len(high_gain_sugs)} 个高增益SUG(增益 ≥ {REQUIRED_SCORE_GAIN:.2f})")
+
+    # 6.2 添加高分组合(需超过所有来源词得分),并按分数排序
+    combination_candidates: list[tuple[Q, DomainCombination]] = []
+    for comb in domain_combinations:
+        if comb.is_above_source_scores and comb.score_with_o > 0:
+            domains_str = ','.join([f'D{d}' for d in comb.domains]) if comb.domains else ''
+            q = Q(
+                text=comb.text,
+                score_with_o=comb.score_with_o,
+                reason=comb.reason,
+                from_source="domain_comb",
+                type_label=comb.type_label,
+                domain_type=domains_str  # 添加域信息
+            )
+            combination_candidates.append((q, comb))
+
+    combination_candidates.sort(key=lambda item: item[0].score_with_o, reverse=True)
+    q_list_next.extend([item[0] for item in combination_candidates])
+    high_score_combinations = [item[1] for item in combination_candidates]
+    print(f"  添加 {len(high_score_combinations)} 个高分组合(组合得分 > 所有来源词)")
+
+    # 保存round数据(包含完整帖子信息)
+    search_results_data = []
+    for search in search_list:
+        search_results_data.append({
+            "text": search.text,
+            "score_with_o": search.score_with_o,
+            "post_list": [
+                {
+                    "note_id": post.note_id,
+                    "note_url": post.note_url,
+                    "title": post.title,
+                    "body_text": post.body_text,
+                    "images": post.images,
+                    "interact_info": post.interact_info
+                }
+                for post in search.post_list
+            ]
+        })
+
+    round_data.update({
+        "input_queries": [{"text": q.text, "score": q.score_with_o, "from_source": q.from_source, "type": "input", "domain_index": q.domain_index, "domain_type": q.domain_type} for q in query_input],
+        "domain_combinations_count": len(domain_combinations),
+        "domain_combinations": [
+            {
+                "text": comb.text,
+                "type_label": comb.type_label,
+                "score": comb.score_with_o,
+                "reason": comb.reason,
+                "domains": comb.domains,
+                "source_words": comb.source_words,
+                "from_segments": comb.from_segments,
+                "source_word_details": comb.source_word_details,
+                "source_scores": comb.source_scores,
+                "is_above_source_scores": comb.is_above_source_scores,
+                "max_source_score": comb.max_source_score
+            }
+            for comb in domain_combinations
+        ],
+        "high_score_combinations": [
+            {
+                "text": item[0].text,
+                "score": item[0].score_with_o,
+                "type_label": item[0].type_label,
+                "type": "combination",
+                "is_above_source_scores": item[1].is_above_source_scores
+            }
+            for item in combination_candidates
+        ],
+        "sug_count": len(all_sugs),
+        "sug_details": sug_details,
+        "high_score_sug_count": len(high_score_sugs),
+        "high_gain_sugs": [{"text": q.text, "score": q.score_with_o, "type": "sug"} for q in q_list_next if q.from_source == "sug"],
+        "search_count": len(search_list),
+        "search_results": search_results_data,
+        "q_list_next_size": len(q_list_next),
+        "q_list_next_sections": {
+            "sugs": [
+                {
+                    "text": item[0].text,
+                    "score": item[0].score_with_o,
+                    "from_source": "sug"
+                }
+                for item in sug_candidates
+            ],
+            "domain_combinations": [
+                {
+                    "text": item[0].text,
+                    "score": item[0].score_with_o,
+                    "from_source": "domain_comb",
+                    "is_above_source_scores": item[1].is_above_source_scores
+                }
+                for item in combination_candidates
+            ]
+        }
+    })
+    context.rounds.append(round_data)
+
+    print(f"\nRound {round_num} 总结:")
+    print(f"  输入Query数: {len(query_input)}")
+    print(f"  域组合数: {len(domain_combinations)}")
+    print(f"  高分组合: {len(high_score_combinations)}")
+    print(f"  SUG数: {len(all_sugs)}")
+    print(f"  高分SUG数: {len(high_score_sugs)}")
+    print(f"  高增益SUG: {len(high_gain_sugs)}")
+    print(f"  搜索数: {len(search_list)}")
+    print(f"  下轮Query数: {len(q_list_next)}")
+
+    return q_list_next, search_list
+
+
+async def iterative_loop_v2(
+    context: RunContext,
+    max_rounds: int = 4,
+    sug_threshold: float = 0.7
+):
+    """v121 主迭代循环"""
+
+    print(f"\n{'='*60}")
+    print(f"开始v121迭代循环(语义分段跨域组词版)")
+    print(f"最大轮数: {max_rounds}")
+    print(f"sug阈值: {sug_threshold}")
+    print(f"{'='*60}")
+
+    # Round 0: 初始化(语义分段 + 拆词)
+    segments = await initialize_v2(context.o, context)
+
+    # API实例
+    xiaohongshu_api = XiaohongshuSearchRecommendations()
+    xiaohongshu_search = XiaohongshuSearch()
+
+    # 收集所有搜索结果
+    all_search_list = []
+
+    # 准备 Round 1 的输入:从 segments 提取所有 words
+    query_input = extract_words_from_segments(segments)
+    print(f"\n提取了 {len(query_input)} 个词作为 Round 1 的输入")
+
+    # Round 1-N: 迭代循环
+    num_segments = len(segments)
+    actual_max_rounds = min(max_rounds, num_segments)
+    round_num = 1
+
+    while query_input and round_num <= actual_max_rounds:
+        query_input, search_list = await run_round_v2(
+            round_num=round_num,
+            query_input=query_input,  # 传递上一轮的输出
+            segments=segments,
+            o=context.o,
+            context=context,
+            xiaohongshu_api=xiaohongshu_api,
+            xiaohongshu_search=xiaohongshu_search,
+            sug_threshold=sug_threshold
+        )
+
+        all_search_list.extend(search_list)
+
+        # 如果没有新的query,提前结束
+        if not query_input:
+            print(f"\n第{round_num}轮后无新query生成,提前结束迭代")
+            break
+
+        round_num += 1
+
+    print(f"\n{'='*60}")
+    print(f"迭代完成")
+    print(f"  实际轮数: {round_num}")
+    print(f"  总搜索次数: {len(all_search_list)}")
+    print(f"  总帖子数: {sum(len(s.post_list) for s in all_search_list)}")
+    print(f"{'='*60}")
+
+    return all_search_list
+
+
+# ============================================================================
+# 主函数
+# ============================================================================
+
+async def main(input_dir: str, max_rounds: int = 2, sug_threshold: float = 0.7, visualize: bool = False):
+    """主函数"""
+    current_time, log_url = set_trace()
+
+    # 读取输入
+    input_context_file = os.path.join(input_dir, 'context.md')
+    input_q_file = os.path.join(input_dir, 'q.md')
+
+    c = read_file_as_string(input_context_file)  # 原始需求
+    o = read_file_as_string(input_q_file)  # 原始问题
+
+    # 版本信息
+    version = os.path.basename(__file__)
+    version_name = os.path.splitext(version)[0]
+
+    # 日志目录
+    log_dir = os.path.join(input_dir, "output", version_name, current_time)
+
+    # 创建运行上下文
+    run_context = RunContext(
+        version=version,
+        input_files={
+            "input_dir": input_dir,
+            "context_file": input_context_file,
+            "q_file": input_q_file,
+        },
+        c=c,
+        o=o,
+        log_dir=log_dir,
+        log_url=log_url,
+    )
+
+    # 创建日志目录
+    os.makedirs(run_context.log_dir, exist_ok=True)
+
+    # 配置日志文件
+    log_file_path = os.path.join(run_context.log_dir, "run.log")
+    log_file = open(log_file_path, 'w', encoding='utf-8')
+
+    # 重定向stdout到TeeLogger(同时输出到控制台和文件)
+    original_stdout = sys.stdout
+    sys.stdout = TeeLogger(original_stdout, log_file)
+
+    try:
+        print(f"📝 日志文件: {log_file_path}")
+        print(f"{'='*60}\n")
+
+        # 执行迭代 (v121: 使用新架构)
+        all_search_list = await iterative_loop_v2(
+            run_context,
+            max_rounds=max_rounds,
+            sug_threshold=sug_threshold
+        )
+
+        # 格式化输出
+        output = f"原始需求:{run_context.c}\n"
+        output += f"原始问题:{run_context.o}\n"
+        output += f"总搜索次数:{len(all_search_list)}\n"
+        output += f"总帖子数:{sum(len(s.post_list) for s in all_search_list)}\n"
+        output += "\n" + "="*60 + "\n"
+
+        if all_search_list:
+            output += "【搜索结果】\n\n"
+            for idx, search in enumerate(all_search_list, 1):
+                output += f"{idx}. 搜索词: {search.text} (分数: {search.score_with_o:.2f})\n"
+                output += f"   帖子数: {len(search.post_list)}\n"
+                if search.post_list:
+                    for post_idx, post in enumerate(search.post_list[:3], 1):  # 只显示前3个
+                        output += f"   {post_idx}) {post.title}\n"
+                        output += f"      URL: {post.note_url}\n"
+                output += "\n"
+        else:
+            output += "未找到搜索结果\n"
+
+        run_context.final_output = output
+
+        print(f"\n{'='*60}")
+        print("最终结果")
+        print(f"{'='*60}")
+        print(output)
+
+        # 保存上下文文件
+        context_file_path = os.path.join(run_context.log_dir, "run_context.json")
+        context_dict = run_context.model_dump()
+        with open(context_file_path, "w", encoding="utf-8") as f:
+            json.dump(context_dict, f, ensure_ascii=False, indent=2)
+        print(f"\nRunContext saved to: {context_file_path}")
+
+        # 保存详细的搜索结果
+        search_results_path = os.path.join(run_context.log_dir, "search_results.json")
+        search_results_data = [s.model_dump() for s in all_search_list]
+        with open(search_results_path, "w", encoding="utf-8") as f:
+            json.dump(search_results_data, f, ensure_ascii=False, indent=2)
+        print(f"Search results saved to: {search_results_path}")
+
+        # 可视化
+        if visualize:
+            import subprocess
+            output_html = os.path.join(run_context.log_dir, "visualization.html")
+            print(f"\n🎨 生成可视化HTML...")
+
+            # 获取绝对路径
+            abs_context_file = os.path.abspath(context_file_path)
+            abs_output_html = os.path.abspath(output_html)
+
+            # 运行可视化脚本
+            result = subprocess.run([
+                "node",
+                "visualization/sug_v6_1_2_121/index.js",
+                abs_context_file,
+                abs_output_html
+            ])
+
+            if result.returncode == 0:
+                print(f"✅ 可视化已生成: {output_html}")
+            else:
+                print(f"❌ 可视化生成失败")
+
+    finally:
+        # 恢复stdout
+        sys.stdout = original_stdout
+        log_file.close()
+        print(f"\n📝 运行日志已保存: {log_file_path}")
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description="搜索query优化工具 - v6.1.2.121 语义分段跨域组词版")
+    parser.add_argument(
+        "--input-dir",
+        type=str,
+        default="input/旅游-逸趣玩旅行/如何获取能体现川西秋季特色的高质量风光摄影素材?",
+        help="输入目录路径,默认: input/旅游-逸趣玩旅行/如何获取能体现川西秋季特色的高质量风光摄影素材?"
+    )
+    parser.add_argument(
+        "--max-rounds",
+        type=int,
+        default=4,
+        help="最大轮数,默认: 4"
+    )
+    parser.add_argument(
+        "--sug-threshold",
+        type=float,
+        default=0.7,
+        help="suggestion阈值,默认: 0.7"
+    )
+    parser.add_argument(
+        "--visualize",
+        action="store_true",
+        default=True,
+        help="运行完成后自动生成可视化HTML"
+    )
+    args = parser.parse_args()
+
+    asyncio.run(main(args.input_dir, max_rounds=args.max_rounds, sug_threshold=args.sug_threshold, visualize=args.visualize))

+ 3880 - 0
sug_v6_1_2_128.py

@@ -0,0 +1,3880 @@
+import asyncio
+import json
+import os
+import sys
+import argparse
+from datetime import datetime
+from typing import Literal, Optional
+
+from agents import Agent, Runner, ModelSettings
+from lib.my_trace import set_trace
+from pydantic import BaseModel, Field
+
+from lib.utils import read_file_as_string
+from lib.client import get_model
+MODEL_NAME = "google/gemini-2.5-flash"
+# 得分提升阈值:sug或组合词必须比来源query提升至少此幅度才能进入下一轮
+REQUIRED_SCORE_GAIN = 0.02
+from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
+from script.search.xiaohongshu_search import XiaohongshuSearch
+
+
+# ============================================================================
+# 日志工具类
+# ============================================================================
+
+class TeeLogger:
+    """同时输出到控制台和日志文件的工具类"""
+    def __init__(self, stdout, log_file):
+        self.stdout = stdout
+        self.log_file = log_file
+
+    def write(self, message):
+        self.stdout.write(message)
+        self.log_file.write(message)
+        self.log_file.flush()  # 实时写入,避免丢失日志
+
+    def flush(self):
+        self.stdout.flush()
+        self.log_file.flush()
+
+
+# ============================================================================
+# 数据模型
+# ============================================================================
+
+class Seg(BaseModel):
+    """分词(旧版)- v120使用"""
+    text: str
+    score_with_o: float = 0.0  # 与原始问题的评分
+    reason: str = ""  # 评分理由
+    from_o: str = ""  # 原始问题
+
+
+# ============================================================================
+# 新架构数据模型 (v121)
+# ============================================================================
+
+class Segment(BaseModel):
+    """语义片段(Round 0语义分段结果)"""
+    text: str  # 片段文本
+    type: str  # 语义类型: 疑问标记/核心动作/修饰短语/中心名词/逻辑连接
+    score_with_o: float = 0.0  # 与原始问题的评分
+    reason: str = ""  # 评分理由
+    from_o: str = ""  # 原始问题
+    words: list[str] = Field(default_factory=list)  # 该片段拆分出的词列表(Round 0拆词结果)
+    word_scores: dict[str, float] = Field(default_factory=dict)  # 词的评分 {word: score}
+    word_reasons: dict[str, str] = Field(default_factory=dict)  # 词的评分理由 {word: reason}
+
+
+class DomainCombination(BaseModel):
+    """域组合(Round N的N域组合结果)"""
+    text: str  # 组合后的文本
+    domains: list[int] = Field(default_factory=list)  # 参与组合的域索引列表(对应segments的索引)
+    type_label: str = ""  # 类型标签,如 [疑问标记+核心动作+中心名词]
+    source_words: list[list[str]] = Field(default_factory=list)  # 来源词列表,每个元素是一个域的词列表,如 [["猫咪"], ["梗图"]]
+    score_with_o: float = 0.0  # 与原始问题的评分
+    reason: str = ""  # 评分理由
+    from_segments: list[str] = Field(default_factory=list)  # 来源segment的文本列表
+    source_word_details: list[dict] = Field(default_factory=list)  # 词及其得分信息 [{"domain_index":0,"segment_type":"","words":[{"text":"","score":0.0}]}]
+    source_scores: list[float] = Field(default_factory=list)  # 来源词的分数列表(扁平化)
+    max_source_score: float | None = None  # 来源词的最高分
+    is_above_source_scores: bool = False  # 组合得分是否超过所有来源词
+
+
+# ============================================================================
+# 旧架构数据模型(保留但不使用)
+# ============================================================================
+
+# class Word(BaseModel):
+#     """词(旧版)- v120使用,v121不再使用"""
+#     text: str
+#     score_with_o: float = 0.0  # 与原始问题的评分
+#     from_o: str = ""  # 原始问题
+
+
+class Word(BaseModel):
+    """词"""
+    text: str
+    score_with_o: float = 0.0  # 与原始问题的评分
+    from_o: str = ""  # 原始问题
+
+
+class QFromQ(BaseModel):
+    """Q来源信息(用于Sug中记录)"""
+    text: str
+    score_with_o: float = 0.0
+
+
+class Q(BaseModel):
+    """查询"""
+    text: str
+    score_with_o: float = 0.0  # 与原始问题的评分
+    reason: str = ""  # 评分理由
+    from_source: str = ""  # v120: seg/sug/add; v121新增: segment/domain_comb/sug
+    type_label: str = ""  # v121新增:域类型标签(仅用于domain_comb来源)
+    domain_index: int = -1  # v121新增:域索引(word来源时有效,-1表示无域)
+    domain_type: str = ""  # v121新增:域类型(word来源时表示所属segment的type,如"中心名词")
+
+
+class Sug(BaseModel):
+    """建议词"""
+    text: str
+    score_with_o: float = 0.0  # 与原始问题的评分
+    reason: str = ""  # 评分理由
+    from_q: QFromQ | None = None  # 来自的q
+
+
+class Seed(BaseModel):
+    """种子(旧版)- v120使用,v121不再使用"""
+    text: str
+    added_words: list[str] = Field(default_factory=list)  # 已经增加的words
+    from_type: str = ""  # seg/sug/add
+    score_with_o: float = 0.0  # 与原始问题的评分
+
+
+class Post(BaseModel):
+    """帖子"""
+    title: str = ""
+    body_text: str = ""
+    type: str = "normal"  # video/normal
+    images: list[str] = Field(default_factory=list)  # 图片url列表,第一张为封面
+    video: str = ""  # 视频url
+    interact_info: dict = Field(default_factory=dict)  # 互动信息
+    note_id: str = ""
+    note_url: str = ""
+
+
+class Search(Sug):
+    """搜索结果(继承Sug)"""
+    post_list: list[Post] = Field(default_factory=list)  # 搜索得到的帖子列表
+
+
+class RunContext(BaseModel):
+    """运行上下文"""
+    version: str
+    input_files: dict[str, str]
+    c: str  # 原始需求
+    o: str  # 原始问题
+    log_url: str
+    log_dir: str
+
+    # v121新增:语义分段结果
+    segments: list[dict] = Field(default_factory=list)  # Round 0的语义分段结果
+
+    # 每轮的数据
+    rounds: list[dict] = Field(default_factory=list)  # 每轮的详细数据
+
+    # 最终结果
+    final_output: str | None = None
+
+    # 评估缓存:避免重复评估相同文本
+    evaluation_cache: dict[str, tuple[float, str]] = Field(default_factory=dict)
+    # key: 文本, value: (score, reason)
+
+    # 历史词/组合得分追踪(用于Round 2+计算系数)
+    word_score_history: dict[str, float] = Field(default_factory=dict)
+    # key: 词/组合文本, value: 最终得分
+
+
+# ============================================================================
+# Agent 定义
+# ============================================================================
+
+# ============================================================================
+# v121 新增 Agent
+# ============================================================================
+
+# Agent: 语义分段专家 (Prompt1)
+class SemanticSegment(BaseModel):
+    """单个语义片段"""
+    segment_text: str = Field(..., description="片段文本")
+    segment_type: str = Field(..., description="语义类型(疑问标记/核心动作/修饰短语/中心名词/逻辑连接)")
+    reasoning: str = Field(..., description="分段理由")
+
+
+class SemanticSegmentation(BaseModel):
+    """语义分段结果"""
+    segments: list[SemanticSegment] = Field(..., description="语义片段列表")
+    overall_reasoning: str = Field(..., description="整体分段思路")
+
+
+semantic_segmentation_instructions = """
+你是语义分段专家。给定一个搜索query,将其拆分成不同语义类型的片段。
+
+## 语义类型定义
+1. **疑问引导**:如何、怎么、什么、哪里等疑问词
+2. **核心动作**:关键动词,如获取、制作、拍摄、寻找等
+3. **修饰短语**:形容词、副词等修饰成分
+4. **中心名词**:核心名词
+5. **逻辑连接**:并且、或者、以及等连接词(较少出现)
+
+## 分段原则
+1. **语义完整性**:每个片段应该是一个完整的语义单元
+2. **类型互斥**:每个片段只能属于一种类型
+3. **保留原文**:片段文本必须保留原query中的字符,不得改写
+4. **顺序保持**:片段顺序应与原query一致
+
+
+## 输出要求
+- segments: 片段列表
+  - segment_text: 片段文本(必须来自原query)
+  - segment_type: 语义类型(从5种类型中选择)
+  - reasoning: 为什么这样分段
+- overall_reasoning: 整体分段思路
+
+## JSON输出规范
+1. **格式要求**:必须输出标准JSON格式
+2. **引号规范**:字符串中如需表达引用,使用书名号《》或「」,不要使用英文引号或中文引号""
+""".strip()
+
+semantic_segmenter = Agent[None](
+    name="语义分段专家",
+    instructions=semantic_segmentation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=SemanticSegmentation,
+)
+
+
+# ============================================================================
+# v120 保留 Agent
+# ============================================================================
+
+# Agent 1: 分词专家(v121用于Round 0拆词)
+class WordSegmentation(BaseModel):
+    """分词结果"""
+    words: list[str] = Field(..., description="分词结果列表")
+    reasoning: str = Field(..., description="分词理由")
+
+word_segmentation_instructions = """
+你是分词专家。给定一个query,将其拆分成有意义的最小单元。
+
+## 分词原则
+1. 保留有搜索意义的词汇
+2. 拆分成独立的概念
+3. 保留专业术语的完整性
+4. 去除虚词(的、吗、呢等),但保留疑问词(如何、为什么、怎样等)
+
+## 输出要求
+返回分词列表和分词理由。
+""".strip()
+
+word_segmenter = Agent[None](
+    name="分词专家",
+    instructions=word_segmentation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=WordSegmentation,
+)
+
+
+# Agent 2: 动机维度评估专家 + 品类维度评估专家(两阶段评估)
+
+# 动机评估的嵌套模型
+class CoreMotivationExtraction(BaseModel):
+    """核心动机提取"""
+    简要说明核心动机: str = Field(..., description="核心动机说明")
+
+class MotivationEvaluation(BaseModel):
+    """动机维度评估"""
+    原始问题核心动机提取: CoreMotivationExtraction = Field(..., description="原始问题核心动机提取")
+    动机维度得分: float = Field(..., description="动机维度得分 -1~1")
+    简要说明动机维度相关度理由: str = Field(..., description="动机维度相关度理由")
+    得分为零的原因: Optional[Literal["原始问题无动机", "sug词条无动机", "动机不匹配", "不适用"]] = Field(None, description="当得分为0时的原因分类(可选,仅SUG评估使用)")
+
+class CategoryEvaluation(BaseModel):
+    """品类维度评估"""
+    品类维度得分: float = Field(..., description="品类维度得分 -1~1")
+    简要说明品类维度相关度理由: str = Field(..., description="品类维度相关度理由")
+
+class ExtensionWordEvaluation(BaseModel):
+    """延伸词评估"""
+    延伸词得分: float = Field(..., ge=-1, le=1, description="延伸词得分 -1~1")
+    简要说明延伸词维度相关度理由: str = Field(..., description="延伸词维度相关度理由")
+
+# 动机评估 prompt(统一版本)
+motivation_evaluation_instructions = """
+# 角色
+你是**专业的动机意图评估专家**。
+任务:判断<平台sug词条>与<原始问题>的**动机意图匹配度**,给出**-1到1之间**的数值评分。
+
+---
+# 输入信息
+你将接收到以下输入:
+- **<原始问题>**:用户的初始查询问题,代表用户的真实需求意图。
+- **<平台sug词条>**:待评估的词条,可能是单个或多个作用域的组合
+---
+
+
+# 核心约束
+
+## 维度独立性声明
+【严格约束】本评估**仅评估动机意图维度**:
+- **只评估** 用户"想要做什么",即原始问题的行为意图和目的
+- 核心是 **动词**:获取、学习、拍摄、制作、寻找等
+- 包括:核心动作 + 使用场景 + 最终目的
+- **评估重点**:动作本身及其语义方向
+ **禁止使用"主题相关"作为评分依据**:评分理由中不得出现"主题"、"内容"、"话题"等词
+
+---
+
+# 作用域与动作意图
+
+## 什么是作用域?
+**作用域 = 动机层 + 对象层 + 场景层**
+
+## 动作意图的识别
+
+### 方法1: 显性动词直接提取
+
+当原始问题明确包含动词时,直接提取
+示例:
+"如何获取素材" → 核心动机 = "获取"
+"寻找拍摄技巧" → 核心动机 = "寻找"(或"学习")
+"制作视频教程" → 核心动机 = "制作"
+
+### 方法2: 隐性动词语义推理
+当原始问题没有显性动词时,需要结合上下文推理
+
+如果原始问题是纯名词短语,无任何动作线索:
+→ 核心动机 = 无法识别
+→ 在此情况下,动机维度得分应为 0。
+示例:
+"摄影" → 无法识别动机,动机维度得分 = 0
+"川西风光" → 无法识别动机,动机维度得分 = 0
+
+---
+
+# 部分作用域的处理
+
+## 情况1:sug词条是原始问题的部分作用域
+
+当sug词条只包含原始问题的部分作用域时,需要判断:
+1. sug词条是否包含动作意图
+2. 如果包含,动作是否匹配
+
+**示例**:
+```
+原始问题:"川西旅行行程规划"
+- 完整作用域:规划(动作)+ 旅行行程(对象)+ 川西(场景)
+
+Sug词条:"川西旅行"
+- 包含作用域:旅行(部分对象)+ 川西(场景)
+- 缺失作用域:规划(动作)
+- 动作意图评分:0(无动作意图)
+```
+
+**评分原则**:
+- 如果sug词条缺失动机层(动作) → 动作意图得分 = 0
+- 如果sug词条包含动机层 → 按动作匹配度评分
+
+---
+
+# 评分标准
+
+## 【正向匹配】
+
+### +0.9~1.0:核心动作完全一致
+**示例**:
+- "规划旅行行程" vs "安排旅行路线" → 0.98
+  - 规划≈安排,语义完全一致
+- "获取素材" vs "下载素材" → 0.97
+  - 获取≈下载,语义完全一致
+
+- 特殊规则: 如果sug词的核心动作是原始问题动作的**具体化子集**,也判定为完全一致
+例: 原始问题"扣除猫咪主体的方法" vs sug词"扣除猫咪眼睛的方法"(子集但目的一致
+**注意**:此处不考虑对象和场景是否一致,只看动作本身
+
+###+0.75~0.95: 核心动作语义相近或为同义表达
+  - 例: 原始问题"如何获取素材" vs sug词"如何下载素材"
+  - 同义词对: 获取≈下载≈寻找, 技巧≈方法≈教程≈攻略
+
+### +0.50~0.75:动作意图相关
+**判定标准**:
+- 动作是实现原始意图的相关路径
+- 或动作是原始意图的前置/后置步骤
+
+**示例**:
+- "获取素材" vs "管理素材" → 0.65
+  - 管理是获取后的相关步骤
+- "规划行程" vs "预订酒店" → 0.60
+  - 预订是规划的具体实施步骤
+
+### +0.25~0.50:动作意图弱相关
+**判定标准**:
+- 动作在同一大类但方向不同
+- 或动作有间接关联
+
+**示例**:
+- "学习摄影技巧" vs "欣赏摄影作品" → 0.35
+  - 都与摄影有关,但学习≠欣赏
+- "规划旅行" vs "回忆旅行" → 0.30
+  - 都与旅行有关,但方向不同
+
+---
+
+## 【中性/无关】
+
+### 0:无动作意图或动作完全无关
+**适用场景**:
+1. 原始问题或sug词条无法识别动作
+2. 两者动作意图完全无关
+
+**示例**:
+- "如何获取素材" vs "摄影器材" → 0
+  - sug词条无动作意图
+- "川西风光" vs "风光摄影作品" → 0
+  - 原始问题无动作意图
+
+**理由模板**:
+- "sug词条无明确动作意图,无法评估动作匹配度"
+- "原始问题无明确动作意图,动作维度得分为0"
+
+---
+
+## 【负向偏离】
+
+### -0.2~-0.05:动作方向轻度偏离
+**示例**:
+- "学习摄影技巧" vs "销售摄影课程" → -0.10
+  - 学习 vs 销售,方向有偏差
+
+### -0.5~-0.25:动作意图明显冲突
+**示例**:
+- "获取免费素材" vs "购买素材" → -0.35
+  - 获取免费 vs 购买,明显冲突
+
+### -1.0~-0.55:动作意图完全相反
+**示例**:
+- "下载素材" vs "上传素材" → -0.70
+  - 下载 vs 上传,方向完全相反
+
+---
+
+## 得分为零的原因(语义判断)
+
+当动机维度得分为 0 时,需要在 `得分为零的原因` 字段中选择以下之一:
+- **"原始问题无动机"**:原始问题是纯名词短语,无法识别任何动作意图
+- **"sug词条无动机"**:sug词条中不包含任何动作意图
+- **"动机不匹配"**:双方都有动作,但完全无关联
+- **"不适用"**:得分不为零时使用此默认值
+
+---
+
+# 输出格式
+输出结果必须为一个 **JSON 格式**,包含以下内容:
+```json
+{
+  "原始问题核心动机提取": {
+    "简要说明核心动机": ""
+  },
+  "动机维度得分": "-1到1之间的小数",
+  "简要说明动机维度相关度理由": "评估该sug词条与原始问题动机匹配程度的理由,包含作用域覆盖情况",
+  "得分为零的原因": "原始问题无动机/sug词条无动机/动机不匹配/不适用"
+}
+```
+
+**输出约束(非常重要)**:
+1. **字符串长度限制**:\"简要说明动机维度相关度理由\"字段必须控制在**150字以内**
+2. **JSON格式规范**:必须生成完整的JSON格式,确保字符串用双引号包裹且正确闭合
+3. **引号使用**:字符串中如需表达引用,请使用《》或「」代替单引号或双引号
+
+---
+
+# 核心原则总结
+1. **只评估动作**:完全聚焦于动作意图,不管对象和场景
+2. **作用域识别**:识别作用域但只评估动机层
+3. **严格标准一致性**:对所有用例使用相同的评估标准,避免评分飘移
+4. **理由纯粹**:评分理由只能谈动作,不能谈对象、场景、主题
+""".strip()
+
+# 品类评估 prompt
+category_evaluation_instructions = """
+# 角色
+你是**专业的内容主体评估专家**。
+任务:判断<平台sug词条>与<原始问题>的**内容主体匹配度**,给出**-1到1之间**的数值评分。
+
+---
+
+# 输入信息
+- **<原始问题>**:用户的完整需求描述
+- **<平台sug词条>**:待评估的词条,可能是单个或多个作用域的组合
+---
+
+
+# 核心约束
+
+## 维度独立性声明
+【严格约束】本评估**仅评估内容主体维度**:
+- **只评估**:名词主体 + 限定词(地域、时间、场景、质量等)
+- **完全忽略**:动作、意图、目的
+- **评估重点**:内容本身的主题和属性
+
+---
+
+# 作用域与内容主体
+
+## 什么是作用域?
+**作用域 = 动机层 + 对象层 + 场景层**
+
+在Prompt2中:
+- **动机层(动作)完全忽略**
+- **只评估对象层 + 场景层(限定词)**
+
+## 内容主体的构成
+
+**内容主体 = 核心名词 + 限定词**
+
+
+---
+
+# 作用域覆盖度评估
+
+## 核心原则:越完整越高分
+
+**完整性公式**:
+```
+作用域覆盖度 = sug词条包含的作用域元素 / 原始问题的作用域元素总数
+```
+
+**评分影响**:
+- 覆盖度100% → 基础高分(0.9+)
+- 覆盖度50-99% → 中高分(0.6-0.9)
+- 覆盖度<50% → 中低分(0.3-0.6)
+- 覆盖度=0 → 低分或0分
+
+---
+
+## 部分作用域的处理
+
+### 情况1:sug词条包含原始问题的所有对象层和场景层元素
+**评分**:0.95-1.0
+
+**示例**:
+```
+原始问题:"川西秋季风光摄影素材"
+- 对象层:摄影素材
+- 场景层:川西 + 秋季 + 风光
+
+Sug词条:"川西秋季风光摄影作品"
+- 对象层:摄影作品(≈素材)
+- 场景层:川西 + 秋季 + 风光
+- 覆盖度:100%
+- 评分:0.98
+```
+
+### 情况2:sug词条包含部分场景层元素
+**评分**:根据覆盖比例
+
+**示例**:
+```
+原始问题:"川西秋季风光摄影素材"
+- 对象层:摄影素材
+- 场景层:川西 + 秋季 + 风光(3个元素)
+
+Sug词条:"川西风光摄影素材"
+- 对象层:摄影素材 ✓
+- 场景层:川西 + 风光(2个元素)
+- 覆盖度:(1+2)/(1+3) = 75%
+- 评分:0.85
+```
+
+### 情况3:sug词条只包含对象层,无场景层
+**评分**:根据对象匹配度和覆盖度
+
+**示例**:
+```
+原始问题:"川西秋季风光摄影素材"
+- 对象层:摄影素材
+- 场景层:川西 + 秋季 + 风光
+
+Sug词条:"摄影素材"
+- 对象层:摄影素材 ✓
+- 场景层:无
+- 覆盖度:1/4 = 25%
+- 评分:0.50(对象匹配但缺失所有限定)
+```
+
+### 情况4:sug词条只包含场景层,无对象层
+**评分**:较低分
+
+**示例**:
+```
+原始问题:"川西旅行行程规划"
+- 对象层:旅行行程
+- 场景层:川西
+
+Sug词条:"川西"
+- 对象层:无
+- 场景层:川西 ✓
+- 覆盖度:1/2 = 50%
+- 评分:0.35(只有场景,缺失核心对象)
+```
+
+---
+
+# 评估核心原则
+
+## 原则1:只看表面词汇,禁止联想推演
+**严格约束**:只能基于sug词实际包含的词汇评分
+
+**错误案例**:
+- ❌ "川西旅行" vs "旅行"
+  - 错误:"旅行可以包括川西,所以有关联" → 评分0.7
+  - 正确:"sug词只有'旅行',无'川西',缺失地域限定" → 评分0.50
+
+
+---
+
+# 评分标准
+
+## 【正向匹配】
+
++0.95~1.0: 核心主体+所有关键限定词完全匹配
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"川西秋季风光摄影作品"
+
++0.75~0.95: 核心主体匹配,存在限定词匹配
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"川西风光摄影素材"(缺失"秋季")
+
++0.5~0.75: 核心主体匹配,无限定词匹配或合理泛化
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"四川风光摄影"
+
++0.3~0.5: 核心主体匹配,但限定词缺失或存在语义错位
+  - 特别注意"语义身份"差异,主体词出现但上下文语义不同
+  - 例:
+    · "猫咪的XX行为"(猫咪是行为者)
+    · vs "用猫咪表达XX的梗图"(猫咪是媒介)
+    · 虽都含"猫咪+XX",但语义角色不同
+
++0.2~0.3: 主体词不匹配,限定词缺失或错位
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"风光摄影入门"
+
++0.05~0.2: 主体词过度泛化或仅抽象相似
+  - 例: sug词是通用概念,原始问题是特定概念
+    sug词"每日计划"(通用)vs 原始问题 "川西旅行行程"(特定)
+      → 评分:0.08
+
+【中性/无关】
+0: 类别明显不同,没有明确目的,无明确关联
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"人像摄影素材"
+  - 例: 原始问题无法识别动机 且 sug词也无明确动作 → 0
+
+【负向偏离】
+-0.2~-0.05: 主体词或限定词存在误导性
+  - 例: 原始问题"免费摄影素材" vs sug词"付费摄影素材库"
+
+-0.5~-0.25: 主体词明显错位或品类冲突
+  - 例: 原始问题"风光摄影素材" vs sug词"人像修图教程"
+
+-1.0~-0.55: 完全错误的品类或有害引导
+  - 例: 原始问题"正版素材获取" vs sug词"盗版素材下载"
+
+
+---
+
+# 输出格式
+输出结果必须为一个 **JSON 格式**,包含以下内容:
+```json
+{
+  "品类维度得分": "-1到1之间的小数",
+  "简要说明品类维度相关度理由": "评估该sug词条与原始问题品类匹配程度的理由,包含作用域覆盖理由"
+}
+```
+
+**输出约束(非常重要)**:
+1. **字符串长度限制**:\"简要说明品类维度相关度理由\"字段必须控制在**150字以内**
+2. **JSON格式规范**:必须生成完整的JSON格式,确保字符串用双引号包裹且正确闭合
+3. **引号使用**:字符串中如需表达引用,请使用《》或「」代替单引号或双引号
+
+---
+
+# 核心原则总结
+
+1. **只看名词和限定词**:完全忽略动作和意图
+2. **作用域覆盖优先**:覆盖的作用域元素越多,分数越高
+3. **禁止联想推演**:只看sug词实际包含的词汇
+4. **通用≠特定**:通用概念不等于特定概念
+5. **理由纯粹**:评分理由只能谈对象、限定词、覆盖度
+""".strip()
+
+# 延伸词评估 prompt
+extension_word_evaluation_instructions = """
+# 角色
+你是**专业的延伸词语义评估专家**。
+任务:识别<平台sug词条>中的延伸词,评估其对原始问题作用域的补全度和目的贡献度,给出**-1到1之间**的数值评分。
+
+---
+# 输入信息
+- **<原始问题>**:用户的完整需求描述
+- **<平台sug词条>**:待评估的词条,可能是单个或多个作用域的组合
+---
+
+# 核心概念
+
+## 什么是延伸词?
+**延伸词**:<平台sug词条>中出现,但不属于<原始问题>作用域范围内的词汇或概念
+
+**关键判断**:
+```
+IF sug词的词汇属于原始问题的作用域元素(动机/对象/场景):
+   → 不是延伸词,是作用域内的词
+
+IF sug词的词汇不属于原始问题的作用域:
+   → 是延伸词
+   → 由Prompt3评估
+```
+
+---
+
+# 作用域与延伸词
+
+## 作用域
+**作用域 = 动机层 + 对象层 + 场景层**
+
+**非延伸词示例**(属于作用域内):
+```
+原始问题:"川西旅行行程规划"
+作用域:
+- 动机层:规划
+- 对象层:旅行行程
+- 场景层:川西
+
+Sug词条:"川西旅行行程规划攻略"
+- "川西"→ 属于场景层,不是延伸词
+- "旅行"→ 属于对象层,不是延伸词
+- "行程"→ 属于对象层,不是延伸词
+- "规划"→ 属于动机层,不是延伸词
+- "攻略"→ 与"规划"同义,不是延伸词
+- 结论:无延伸词
+```
+
+**延伸词示例**(不属于作用域):
+```
+原始问题:"川西旅行行程规划"
+作用域:规划 + 旅行行程 + 川西
+
+Sug词条:"川西旅行行程规划住宿推荐"
+- "住宿推荐"→ 不属于原始问题任何作用域
+- 结论:延伸词 = ["住宿推荐"]
+```
+
+---
+
+# 延伸词识别方法
+
+## 步骤1:提取原始问题的作用域元素
+```
+动机层:提取动作及其同义词
+对象层:提取核心名词及其同义词
+场景层:提取所有限定词
+```
+
+## 步骤2:提取sug词条的所有关键词
+```
+提取sug词条中的所有实词(名词、动词、形容词)
+```
+
+## 步骤3:匹配判定
+```
+FOR 每个sug词条关键词:
+   IF 该词 ∈ 原始问题作用域元素(包括同义词):
+      → 不是延伸词
+   ELSE:
+      → 是延伸词
+```
+
+## 步骤4:同义词/相近词判定规则
+
+### 不算延伸词的情况:
+**同义词**:
+- 行程 ≈ 路线 ≈ 安排 ≈ 计划
+- 获取 ≈ 下载 ≈ 寻找 ≈ 收集
+- 技巧 ≈ 方法 ≈ 教程 ≈ 攻略
+- 素材 ≈ 资源 ≈ 作品 ≈ 内容
+
+**具体化/细化**:
+- 原始:"川西旅游" + sug词:"稻城亚丁"(川西的具体地点)→ 不算延伸
+- 原始:"摄影技巧" + sug词:"风光摄影"(摄影的细化)→ 不算延伸
+- 原始:"素材" + sug词:"高清素材"(素材的质量细化)→ 不算延伸
+
+**判定逻辑**:
+```
+IF sug词的概念是原始问题概念的子集/下位词/同义词:
+   → 不算延伸词
+   → 视为对原问题的细化或重述
+```
+
+---
+
+### 算延伸词的情况:
+
+**新增维度**:原始问题未涉及的信息维度
+- 原始:"川西旅行" + sug词:"住宿" → 延伸词
+- 原始:"摄影素材" + sug词:"版权" → 延伸词
+
+**新增限定条件**:原始问题未提及的约束
+- 原始:"素材获取" + sug词:"免费" → 延伸词
+- 原始:"旅行行程" + sug词:"7天" → 延伸词
+
+**扩展主题**:相关但非原问题范围
+- 原始:"川西行程" + sug词:"美食推荐" → 延伸词
+- 原始:"摄影技巧" + sug词:"后期修图" → 延伸词
+
+**工具/方法**:原始问题未提及的具体工具
+- 原始:"视频剪辑" + sug词:"PR软件" → 延伸词
+- 原始:"图片处理" + sug词:"PS教程" → 延伸词
+
+---
+
+# 延伸词类型与评分
+
+## 核心评估维度:对原始问题作用域的贡献
+
+### 维度1:作用域补全度
+延伸词是否帮助sug词条更接近原始问题的完整作用域?
+
+
+### 维度2:目的达成度
+延伸词是否促进原始问题核心目的的达成?
+---
+####类型1:作用域增强型
+**定义**:延伸词是原始问题核心目的,或补全关键作用域
+**得分范围**:+0.12~+0.20
+
+**判定标准**:
+- 使sug词条更接近原始问题的完整需求
+---
+
+####类型2:作用域辅助型
+**定义**:延伸词对核心目的有辅助作用,但非必需
+
+**得分范围**:+0.05~+0.12
+
+**判定标准**:
+- sug词条更丰富但不改变原始需求核心
+
+---
+
+####类型3:作用域无关型
+**定义**:延伸词与核心目的无实质关联
+
+**得分**:0
+
+**示例**:
+- 原始:"如何拍摄风光" + 延伸词:"相机品牌排行"
+  - 评分:0
+  - 理由:品牌排行与拍摄技巧无关
+
+---
+
+####类型4:作用域稀释型(轻度负向)
+**定义**:延伸词稀释原始问题的聚焦度,降低内容针对性
+
+**得分范围**:-0.08~-0.18
+
+**判定标准**:
+- 引入无关信息,分散注意力
+- 降低内容的专注度和深度
+- 使sug词条偏离原始问题的核心
+
+**示例**:
+- 原始:"专业风光摄影技巧" + 延伸词:"手机拍照"
+  - 评分:-0.12
+  - 理由:手机拍照与专业摄影需求不符,稀释专业度
+
+- 原始:"川西深度游攻略" + 延伸词:"周边一日游"
+  - 评分:-0.10
+  - 理由:一日游与深度游定位冲突,稀释深度
+
+
+---
+
+# 特殊情况处理
+
+## 情况1:多个延伸词同时存在
+**处理方法**:分别评估每个延伸词,然后综合
+
+**综合规则**:
+```
+延伸词总得分 = Σ(每个延伸词得分) / 延伸词数量
+
+考虑累积效应:
+- 多个增强型延伸词 → 总分可能超过单个最高分,但上限+0.25
+- 正负延伸词并存 → 相互抵消
+- 多个冲突型延伸词 → 总分下限-0.60
+```
+
+**示例**:
+```
+原始:"川西旅行行程"
+Sug词条:"川西旅行行程住宿美食推荐"
+延伸词识别:
+- "住宿推荐"→ 增强型,+0.18
+- "美食推荐"→ 辅助型,+0.10
+总得分:(0.18 + 0.10) / 2 = 0.14
+```
+
+---
+
+## 情况2:无延伸词
+**处理方法**:
+```
+IF sug词条无延伸词:
+   延伸词得分 = 0
+   理由:"sug词条未引入延伸词,所有词汇均属于原始问题作用域范围"
+```
+
+---
+
+## 情况3:延伸词使sug词条更接近原始问题
+**特殊加成**:
+```
+IF 延伸词是原始问题隐含需求的显式化:
+   → 额外加成 +0.05
+```
+
+**示例**:
+```
+原始:"川西旅行" (隐含需要行程规划)
+Sug词条:"川西旅行行程规划"
+- "行程规划"可能被识别为延伸词,但它显式化了隐含需求
+- 给予额外加成
+```
+
+---
+
+# 输出格式
+输出结果必须为一个 **JSON 格式**,包含以下内容:
+```json
+{
+  "延伸词得分": "-1到1之间的小数",
+  "简要说明延伸词维度相关度理由": "评估延伸词对作用域的影响"
+}
+```
+
+**输出约束(非常重要)**:
+1. **字符串长度限制**:\"简要说明延伸词维度相关度理由\"字段必须控制在**150字以内**
+2. **JSON格式规范**:必须生成完整的JSON格式,确保字符串用双引号包裹且正确闭合
+3. **引号使用**:字符串中如需表达引用,请使用《》或「」代替单引号或双引号
+
+---
+
+# 核心原则总结
+
+1. **严格区分**:作用域内的词 ≠ 延伸词
+2. **同义词/细化词不算延伸**:属于作用域范围的词由其他prompt评估
+3. **作用域导向**:评估延伸词是否使sug词条更接近原始问题的完整作用域
+4. **目的导向**:评估延伸词是否促进核心目的达成
+5. **分类明确**:准确判定延伸词类型
+6. **理由充分**:每个延伸词都要说明其对作用域和目的的影响
+7. **谨慎负分**:仅在明确冲突或有害时使用负分
+""".strip()
+
+# 创建评估 Agent
+motivation_evaluator = Agent[None](
+    name="动机维度评估专家(后续轮次)",
+    instructions=motivation_evaluation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=MotivationEvaluation)
+
+category_evaluator = Agent[None](
+    name="品类维度评估专家",
+    instructions=category_evaluation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=CategoryEvaluation
+)
+
+extension_word_evaluator = Agent[None](
+    name="延伸词评估专家",
+    instructions=extension_word_evaluation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=ExtensionWordEvaluation,
+    model_settings=ModelSettings(temperature=0.2)
+)
+
+
+# ============================================================================
+# Round 0 专用 Agent(v124新增 - 需求1)
+# ============================================================================
+
+# Round 0 动机评估 prompt(不含延伸词)
+round0_motivation_evaluation_instructions = """
+#角色
+你是**专业的动机意图评估专家**
+你的任务是:判断我给你的 <词条> 与 <原始问题> 的需求动机匹配度,给出 **-1 到 1 之间** 的数值评分。
+
+---
+# 输入信息
+你将接收到以下输入:
+- **<原始问题>**:用户的初始查询问题,代表用户的真实需求意图。
+- **<词条>**:平台推荐的词条列表,每个词条需要单独评估。
+
+# 核心约束
+
+## 维度独立性声明
+【严格约束】本评估**仅评估动机意图维度**:
+- **只评估** 用户"想要做什么",即原始问题的行为意图和目的
+- 核心是 **动词**:获取、学习、拍摄、制作、寻找等
+- 包括:核心动作 + 使用场景 + 最终目的
+- **评估重点**:动作本身及其语义方向
+ **禁止使用"主题相关"作为评分依据**:评分理由中不得出现"主题"、"内容"、"话题"等词
+
+---
+
+# 作用域与动作意图
+
+## 什么是作用域?
+**作用域 = 动机层 + 对象层 + 场景层**
+
+## 动作意图的识别
+
+### 方法1: 显性动词直接提取
+
+当原始问题明确包含动词时,直接提取
+示例:
+"如何获取素材" → 核心动机 = "获取"
+"寻找拍摄技巧" → 核心动机 = "寻找"(或"学习")
+"制作视频教程" → 核心动机 = "制作"
+
+### 方法2: 隐性动词语义推理
+当原始问题没有显性动词时,需要结合上下文推理
+
+如果原始问题是纯名词短语,无任何动作线索:
+→ 核心动机 = 无法识别
+→ 在此情况下,动机维度得分应为 0。
+示例:
+"摄影" → 无法识别动机,动机维度得分 = 0
+"川西风光" → 无法识别动机,动机维度得分 = 0
+
+---
+
+# 部分作用域的处理
+
+## 情况1:词条是原始问题的部分作用域
+
+当词条只包含原始问题的部分作用域时,需要判断:
+1. 词条是否包含动作意图
+2. 如果包含,动作是否匹配
+
+**示例**:
+```
+原始问题:"川西旅行行程规划"
+- 完整作用域:规划(动作)+ 旅行行程(对象)+ 川西(场景)
+
+词条:"川西旅行"
+- 包含作用域:旅行(部分对象)+ 川西(场景)
+- 缺失作用域:规划(动作)
+- 动作意图评分:0(无动作意图)
+```
+
+**评分原则**:
+- 如果sug词条缺失动机层(动作) → 动作意图得分 = 0
+- 如果sug词条包含动机层 → 按动作匹配度评分
+
+
+---
+
+#评分标准:
+
+【正向匹配】
+### +0.9~1.0:核心动作完全一致
+**示例**:
+- "规划旅行行程" vs "安排旅行路线" → 0.98
+  - 规划≈安排,语义完全一致
+- "获取素材" vs "下载素材" → 0.97
+  - 获取≈下载,语义完全一致
+
+- 特殊规则: 如果sug词的核心动作是原始问题动作的**具体化子集**,也判定为完全一致
+例: 原始问题"扣除猫咪主体的方法" vs 词条"扣除猫咪眼睛的方法"(子集但目的一致
+**注意**:此处不考虑对象和场景是否一致,只看动作本身
+
+###+0.75~0.90: 核心动作语义相近或为同义表达
+  - 例: 原始问题"如何获取素材" vs 词条"如何下载素材"
+  - 同义词对: 获取≈下载≈寻找, 技巧≈方法≈教程≈攻略
+
+### +0.50~0.75:动作意图相关
+**判定标准**:
+- 动作是实现原始意图的相关路径
+- 或动作是原始意图的前置/后置步骤
+
+**示例**:
+- "获取素材" vs "管理素材" → 0.65
+  - 管理是获取后的相关步骤
+- "规划行程" vs "预订酒店" → 0.60
+  - 预订是规划的具体实施步骤
+
+### +0.25~0.50:动作意图弱相关
+**判定标准**:
+- 动作在同一大类但方向不同
+- 或动作有间接关联
+
+**示例**:
+- "学习摄影技巧" vs "欣赏摄影作品" → 0.35
+  - 都与摄影有关,但学习≠欣赏
+- "规划旅行" vs "回忆旅行" → 0.30
+  - 都与旅行有关,但方向不同
+
+---
+
+## 【中性/无关】
+
+### 0:无动作意图或动作完全无关
+**适用场景**:
+1. 原始问题或词条无法识别动作
+2. 两者动作意图完全无关
+
+**示例**:
+- "如何获取素材" vs "摄影器材" → 0
+  - sug词条无动作意图
+- "川西风光" vs "风光摄影作品" → 0
+  - 原始问题无动作意图
+
+**理由模板**:
+- "sug词条无明确动作意图,无法评估动作匹配度"
+- "原始问题无明确动作意图,动作维度得分为0"
+
+---
+
+## 【负向偏离】
+
+### -0.2~-0.05:动作方向轻度偏离
+**示例**:
+- "学习摄影技巧" vs "销售摄影课程" → -0.10
+  - 学习 vs 销售,方向有偏差
+
+### -0.5~-0.25:动作意图明显冲突
+**示例**:
+- "获取免费素材" vs "购买素材" → -0.35
+  - 获取免费 vs 购买,明显冲突
+
+### -1.0~-0.55:动作意图完全相反
+**示例**:
+- "下载素材" vs "上传素材" → -0.70
+  - 下载 vs 上传,方向完全相反
+
+---
+
+# 输出要求
+
+输出结果必须为一个 **JSON 格式**,包含以下内容:
+```json
+{
+  "原始问题核心动机提取": {
+    "简要说明核心动机": ""
+  },
+  "动机维度得分": "-1到1之间的小数",
+  "简要说明动机维度相关度理由": "评估该词条与原始问题动机匹配程度的理由"
+}
+```
+
+#注意事项:
+始终围绕动机维度:所有评估都基于"动机"维度,不偏离
+核心动机必须是动词:在评估前,必须先提取原始问题的核心动机(动词),这是整个评估的基础
+严格标准一致性:对所有用例使用相同的评估标准,避免评分飘移
+负分使用原则:仅当词条对原始问题动机产生误导、冲突或有害引导时给予负分
+零分使用原则:当词条与原始问题动机无明确关联,既不相关也不冲突时给予零分,或原始问题无法识别动机时。
+""".strip()
+
+# Round 0 品类评估 prompt(不含延伸词)
+round0_category_evaluation_instructions = """
+#角色
+你是一个 **专业的语言专家和语义相关性评判专家**。
+你的任务是:判断我给你的 <词条> 与 <原始问题> 的内容主体和限定词匹配度,给出 **-1 到 1 之间** 的数值评分。
+
+---
+# 核心概念与方法论
+
+## 评估维度
+本评估系统围绕 **品类维度** 进行:
+
+#  维度独立性警告
+【严格约束】本评估**只评估品类维度**,,必须遵守以下规则:
+1. **只看名词和限定词**:评估时只考虑主体、限定词的匹配度
+2. **完全忽略动词**:动作意图、目的等动机信息对本维度评分无影响
+
+### 品类维度
+**定义:** 用户"关于什么内容",即原始问题的主题对象和限定词
+- 核心是 **名词+限定词**:川西秋季风光摄影素材
+- 包括:核心主体 + 地域限定 + 时间限定 + 质量限定等
+
+## ⚠️ 品类评估核心原则(必读)
+
+### 原则1:只看词条表面,禁止联想推演
+- 只能基于词条实际包含的词汇评分
+- 禁止推测"可能包含"、"可以理解为"
+
+**错误示例:**
+原始问题:"川西旅行行程" vs 词条:"每日计划"
+- 错误 "每日计划可以包含旅行规划,所以有关联" → 这是不允许的联想
+- 正确: "词条只有'每日计划',无'旅行'字眼,品类不匹配" → 正确判断
+
+### 原则2:通用概念 ≠ 特定概念
+- **通用**:计划、方法、技巧、素材(无领域限定)
+- **特定**:旅行行程、摄影技巧、烘焙方法(有明确领域)
+
+IF 词条是通用 且 原始问题是特定:
+   → 品类不匹配 → 评分0.05~0.1
+关键:通用概念不等于特定概念,不能因为"抽象上都是规划"就给分
+
+---
+
+# 输入信息
+你将接收到以下输入:
+- **<原始问题>**:用户的初始查询问题,代表用户的真实需求意图。
+- **<词条>**:平台推荐的词条列表,每个词条需要单独评估。
+
+
+#判定流程
+#评估架构
+
+输入: <原始问题> + <词条>
+         ↓
+【品类维度相关性判定】
+    ├→ 步骤1: 评估<词条>与<原始问题>的内容主体和限定词匹配度
+    └→ 输出: -1到1之间的数值 + 判定依据
+
+
+相关度评估维度详解
+维度2: 品类维度评估
+评估对象: <词条> 与 <原始问题> 的内容主体和限定词匹配度
+
+评分标准:
+
+【正向匹配】
++0.95~1.0: 核心主体+所有关键限定词完全匹配
+  - 例: 原始问题"川西秋季风光摄影素材" vs 词条"川西秋季风光摄影作品"
+
++0.75~0.95: 核心主体匹配,存在限定词匹配
+  - 例: 原始问题"川西秋季风光摄影素材" vs 词条"川西风光摄影素材"(缺失"秋季")
+
++0.5~0.75: 核心主体匹配,无限定词匹配或合理泛化
+  - 例: 原始问题"川西秋季风光摄影素材" vs 词条"四川风光摄影"
+
++0.3~0.5: 核心主体匹配,但限定词缺失或存在语义错位
+  - 特别注意"语义身份"差异,主体词出现但上下文语义不同
+  - 例:
+    · "猫咪的XX行为"(猫咪是行为者)
+    · vs "用猫咪表达XX的梗图"(猫咪是媒介)
+    · 虽都含"猫咪+XX",但语义角色不同
+
++0.2~0.3: 主体词不匹配,限定词缺失或错位
+  - 例: 原始问题"川西秋季风光摄影素材" vs 词条"风光摄影入门"
+
++0.05~0.2: 主体词过度泛化或仅抽象相似
+  - 例: 词条是通用概念,原始问题是特定概念
+    词条"每日计划"(通用)vs 原始问题 "川西旅行行程"(特定)
+      → 评分:0.08
+
+【中性/无关】
+0: 类别明显不同,没有明确目的,无明确关联
+  - 例: 原始问题"川西秋季风光摄影素材" vs 词条"人像摄影素材"
+  - 例: 原始问题无法识别动机 且 词条也无明确动作 → 0
+
+【负向偏离】
+-0.2~-0.05: 主体词或限定词存在误导性
+  - 例: 原始问题"免费摄影素材" vs 词条"付费摄影素材库"
+
+-0.5~-0.25: 主体词明显错位或品类冲突
+  - 例: 原始问题"风光摄影素材" vs 词条"人像修图教程"
+
+-1.0~-0.55: 完全错误的品类或有害引导
+  - 例: 原始问题"正版素材获取" vs 词条"盗版素材下载"
+
+---
+
+# 输出要求
+
+输出结果必须为一个 **JSON 格式**,包含以下内容:
+```json
+{
+  "品类维度得分": "-1到1之间的小数",
+  "简要说明品类维度相关度理由": "评估该词条与原始问题品类匹配程度的理由"
+}
+```
+---
+
+#注意事项:
+始终围绕品类维度:所有评估都基于"品类"维度,不偏离
+严格标准一致性:对所有用例使用相同的评估标准,避免评分飘移
+负分使用原则:仅当词条对原始问题品类产生误导、冲突或有害引导时给予负分
+零分使用原则:当词条与原始问题品类无明确关联,既不相关也不冲突时给予零分
+""".strip()
+
+# 创建 Round 0 评估 Agent
+round0_motivation_evaluator = Agent[None](
+    name="Round 0动机维度评估专家",
+    instructions=round0_motivation_evaluation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=MotivationEvaluation,
+    model_settings=ModelSettings(temperature=0.2)
+)
+
+round0_category_evaluator = Agent[None](
+    name="Round 0品类维度评估专家",
+    instructions=round0_category_evaluation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=CategoryEvaluation,
+    model_settings=ModelSettings(temperature=0.2)
+)
+
+
+# ============================================================================
+# 域内/域间 专用 Agent(v124新增 - 需求2&3)
+# ============================================================================
+
+# 域内/域间 动机评估 prompt(不含延伸词)
+scope_motivation_evaluation_instructions = """
+# 角色
+你是**专业的动机意图评估专家**。
+任务:判断<词条>与<同一作用域词条>的**动机意图匹配度**,给出**-1到1之间**的数值评分。
+
+---
+# 输入信息
+你将接收到以下输入:
+ **<同一作用域词条>**:用户的初始查询问题,代表用户的真实需求意图。
+- **<词条>**:平台推荐的词条列表,每个词条需要单独评估。
+---
+# 评估架构
+
+输入: <同一作用域词条> + <词条>
+         ↓
+【动机维度相关性判定】
+    ├→ 步骤1: 评估<词条>与<同一作用域词条>的需求动机匹配度
+    └→ 输出: -1到1之间的数值 + 判定依据
+
+# 核心约束
+## 维度独立性声明
+【严格约束】本评估**仅评估动机意图维度**:
+- **只评估** 用户"想要做什么",即原始问题的行为意图和目的
+- 核心是 **动词**:获取、学习、拍摄、制作、寻找等
+- 包括:核心动作 + 使用场景 + 最终目的
+- **评估重点**:动作本身及其语义方向
+ **禁止使用"主题相关"作为评分依据**:评分理由中不得出现"主题"、"内容"、"话题"等词
+
+---
+
+# 作用域与动作意图
+
+## 什么是作用域?
+**作用域 = 动机层 + 对象层 + 场景层**
+
+当前任务:
+- **只提取动机层**:动作意图(获取、学习、规划、拍摄等)
+
+## 动作意图的识别
+
+### 1. 动机维度
+**定义:** 用户"想要做什么",即原始问题的行为意图和目的
+- 核心是 **动词**:获取、学习、拍摄、制作、寻找等
+- 包括:核心动作 + 使用场景 + 最终目的
+
+### 方法1: 显性动词直接提取
+
+当原始问题明确包含动词时,直接提取
+示例:
+"如何获取素材" → 核心动机 = "获取"
+"寻找拍摄技巧" → 核心动机 = "寻找"(或"学习")
+"制作视频教程" → 核心动机 = "制作"
+
+### 方法2: 隐性动词语义推理
+当原始问题没有显性动词时,需要结合上下文推理
+
+
+---
+
+# 评分标准
+
+## 【正向匹配】
+
+### +0.9~1.0:核心动作完全一致
+**示例**:
+- "规划旅行行程" vs "安排旅行路线" → 0.98
+  - 规划≈安排,语义完全一致
+- "获取素材" vs "下载素材" → 0.97
+  - 获取≈下载,语义完全一致
+
+- 特殊规则: 如果sug词的核心动作是原始问题动作的**具体化子集**,也判定为完全一致
+例: 原始问题"扣除猫咪主体的方法" vs sug词"扣除猫咪眼睛的方法"(子集但目的一致
+**注意**:此处不考虑对象和场景是否一致,只看动作本身
+
+###+0.75~0.95: 核心动作语义相近或为同义表达
+  - 例: 原始问题"如何获取素材" vs sug词"如何下载素材"
+  - 同义词对: 获取≈下载≈寻找, 技巧≈方法≈教程≈攻略
+
+### +0.50~0.75:动作意图相关
+**判定标准**:
+- 动作是实现原始意图的相关路径
+- 或动作是原始意图的前置/后置步骤
+
+**示例**:
+- "获取素材" vs "管理素材" → 0.65
+  - 管理是获取后的相关步骤
+- "规划行程" vs "预订酒店" → 0.60
+  - 预订是规划的具体实施步骤
+
+### +0.25~0.50:动作意图弱相关
+**判定标准**:
+- 动作在同一大类但方向不同
+- 或动作有间接关联
+
+**示例**:
+- "学习摄影技巧" vs "欣赏摄影作品" → 0.35
+  - 都与摄影有关,但学习≠欣赏
+- "规划旅行" vs "回忆旅行" → 0.30
+  - 都与旅行有关,但方向不同
+
+---
+
+## 【中性/无关】
+
+### 0:无动作意图或动作完全无关
+**适用场景**:
+1. 原始问题或词条无法识别动作
+2. 两者动作意图完全无关
+
+**示例**:
+- "如何获取素材" vs "摄影器材" → 0
+  - 词条无动作意图
+- "川西风光" vs "风光摄影作品" → 0
+  - 原始问题无动作意图
+
+**理由模板**:
+- "词条无明确动作意图,无法评估动作匹配度"
+- "原始问题无明确动作意图,动作维度得分为0"
+
+---
+
+## 【负向偏离】
+
+### -0.2~-0.05:动作方向轻度偏离
+**示例**:
+- "学习摄影技巧" vs "销售摄影课程" → -0.10
+  - 学习 vs 销售,方向有偏差
+
+### -0.5~-0.25:动作意图明显冲突
+**示例**:
+- "获取免费素材" vs "购买素材" → -0.35
+  - 获取免费 vs 购买,明显冲突
+
+### -1.0~-0.55:动作意图完全相反
+**示例**:
+- "下载素材" vs "上传素材" → -0.70
+  - 下载 vs 上传,方向完全相反
+
+---
+
+# 输出格式
+输出结果必须为一个 **JSON 格式**,包含以下内容:
+```json
+{
+  "原始问题核心动机提取": {
+    "简要说明核心动机": ""
+  },
+  "动机维度得分": "-1到1之间的小数",
+  "简要说明动机维度相关度理由": "评估该词条与该条作用域匹配程度的理由",
+  "得分为零的原因": "原始问题无动机/sug词条无动机/动机不匹配/不适用"
+}
+```
+
+---
+
+# 核心原则总结
+1. **只评估动作**:完全聚焦于动作意图,不管对象和场景
+2. **作用域识别**:识别作用域但只评估动机层
+3. **严格标准一致性**:对所有用例使用相同的评估标准,避免评分飘移
+4. **理由纯粹**:评分理由只能谈动作,不能谈对象、场景、主题
+""".strip()
+
+# 域内/域间 品类评估 prompt(不含延伸词)
+scope_category_evaluation_instructions = """
+#角色
+你是一个 **专业的语言专家和语义相关性评判专家**。
+你的任务是:判断我给你的 <词条> 与 <同一作用域词条> 的内容主体和限定词匹配度,给出 **-1 到 1 之间** 的数值评分。
+
+---
+# 输入信息
+你将接收到以下输入:
+- **<同一作用域词条>**:用户的初始查询问题,代表用户的真实需求意图。
+- **<词条>**:平台推荐的词条列表,每个词条需要单独评估。
+
+---
+#判定流程
+#评估架构
+
+输入: <同一作用域词条> + <词条>
+         ↓
+【品类维度相关性判定】
+    ├→ 步骤1: 评估<词条>与<同一作用域词条>的内容主体和限定词匹配度
+    └→ 输出: -1到1之间的数值 + 判定依据
+
+---
+
+# 核心概念与方法论
+
+## 评估维度
+本评估系统围绕 **品类维度** 进行:
+
+#  维度独立性警告
+【严格约束】本评估**只评估品类维度**,,必须遵守以下规则:
+1. **只看名词和限定词**:评估时只考虑主体、限定词的匹配度
+2. **完全忽略动词**:动作意图、目的等动机信息对本维度评分无影响
+
+### 品类维度
+**定义:** 用户"关于什么内容",即原始问题的主题对象和限定词
+- 核心是 **名词+限定词**:川西秋季风光摄影素材
+- 包括:核心主体 + 地域限定 + 时间限定 + 质量限定等
+
+## ⚠️ 品类评估核心原则(必读)
+
+### 原则1:只看词条表面,禁止联想推演
+- 只能基于sug词实际包含的词汇评分
+- 禁止推测"可能包含"、"可以理解为"
+
+**错误示例:**
+原始问题:"川西旅行行程" vs sug词:"每日计划"
+- 错误 "每日计划可以包含旅行规划,所以有关联" → 这是不允许的联想
+- 正确: "sug词只有'每日计划',无'旅行'字眼,品类不匹配" → 正确判断
+
+### 原则2:通用概念 ≠ 特定概念
+- **通用**:计划、方法、技巧、素材(无领域限定)
+- **特定**:旅行行程、摄影技巧、烘焙方法(有明确领域)
+
+IF sug词是通用 且 原始问题是特定:
+   → 品类不匹配 → 评分0.05~0.1
+关键:通用概念不等于特定概念,不能因为"抽象上都是规划"就给分
+
+---
+#相关度评估维度详解
+
+##评估对象: <词条> 与 <同一作用域词条> 的内容主体和限定词匹配度
+
+评分标准:
+
+【正向匹配】
++0.95~1.0: 核心主体+所有关键限定词完全匹配
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"川西秋季风光摄影作品"
+
++0.75~0.95: 核心主体匹配,存在限定词匹配
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"川西风光摄影素材"(缺失"秋季")
+
++0.5~0.75: 核心主体匹配,无限定词匹配或合理泛化
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"四川风光摄影"
+
++0.3~0.5: 核心主体匹配,但限定词缺失或存在语义错位
+  - 特别注意"语义身份"差异,主体词出现但上下文语义不同
+  - 例:
+    · "猫咪的XX行为"(猫咪是行为者)
+    · vs "用猫咪表达XX的梗图"(猫咪是媒介)
+    · 虽都含"猫咪+XX",但语义角色不同
+
++0.2~0.3: 主体词不匹配,限定词缺失或错位
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"风光摄影入门"
+
++0.05~0.2: 主体词过度泛化或仅抽象相似
+  - 例: sug词是通用概念,原始问题是特定概念
+    sug词"每日计划"(通用)vs 原始问题 "川西旅行行程"(特定)
+      → 评分:0.08
+
+【中性/无关】
+0: 类别明显不同,没有明确目的,无明确关联
+  - 例: 原始问题"川西秋季风光摄影素材" vs sug词"人像摄影素材"
+  - 例: 原始问题无法识别动机 且 sug词也无明确动作 → 0
+
+【负向偏离】
+-0.2~-0.05: 主体词或限定词存在误导性
+  - 例: 原始问题"免费摄影素材" vs sug词"付费摄影素材库"
+
+-0.5~-0.25: 主体词明显错位或品类冲突
+  - 例: 原始问题"风光摄影素材" vs sug词"人像修图教程"
+
+-1.0~-0.55: 完全错误的品类或有害引导
+  - 例: 原始问题"正版素材获取" vs sug词"盗版素材下载"
+
+---
+
+# 输出要求
+
+输出结果必须为一个 **JSON 格式**,包含以下内容:
+```json
+{
+  "品类维度得分": "-1到1之间的小数",
+  "简要说明品类维度相关度理由": "评估该词条与同一作用域词条品类匹配程度的理由"
+}
+```
+---
+
+#注意事项:
+始终围绕品类维度:所有评估都基于"品类"维度,不偏离
+严格标准一致性:对所有用例使用相同的评估标准,避免评分飘移
+负分使用原则:仅当词条对原始问题品类产生误导、冲突或有害引导时给予负分
+零分使用原则:当词条与原始问题品类无明确关联,既不相关也不冲突时给予零分
+""".strip()
+
+# 创建域内/域间评估 Agent
+scope_motivation_evaluator = Agent[None](
+    name="域内动机维度评估专家",
+    instructions=scope_motivation_evaluation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=MotivationEvaluation,
+   model_settings=ModelSettings(temperature=0.2)
+)
+
+scope_category_evaluator = Agent[None](
+    name="域内品类维度评估专家",
+    instructions=scope_category_evaluation_instructions,
+    model=get_model(MODEL_NAME),
+    output_type=CategoryEvaluation,
+    model_settings=ModelSettings(temperature=0.2)
+)
+
+
+# ============================================================================
+# v120 保留但不使用的 Agent(v121不再使用)
+# ============================================================================
+
+# # Agent 3: 加词选择专家(旧版 - v120使用,v121不再使用)
+# class WordCombination(BaseModel):
+#     """单个词组合"""
+#     selected_word: str = Field(..., description="选择的词")
+#     combined_query: str = Field(..., description="组合后的新query")
+#     reasoning: str = Field(..., description="选择理由")
+
+# class WordSelectionTop5(BaseModel):
+#     """加词选择结果(Top 5)"""
+#     combinations: list[WordCombination] = Field(
+#         ...,
+#         description="选择的Top 5组合(不足5个则返回所有)",
+#         min_items=1,
+#         max_items=5
+#     )
+#     overall_reasoning: str = Field(..., description="整体选择思路")
+
+# word_selection_instructions 已删除 (v121不再使用)
+
+# word_selector = Agent[None](
+#     name="加词组合专家",
+#     instructions=word_selection_instructions,
+#     model=get_model(MODEL_NAME),
+#     output_type=WordSelectionTop5,
+#     model_settings=ModelSettings(temperature=0.2),
+# )
+
+
+# ============================================================================
+# 辅助函数
+# ============================================================================
+
+# ============================================================================
+# v121 新增辅助函数
+# ============================================================================
+
+def get_ordered_subsets(words: list[str], min_len: int = 1) -> list[list[str]]:
+    """
+    生成words的所有有序子集(可跳过但不可重排)
+
+    使用 itertools.combinations 生成索引组合,保持原始顺序
+
+    Args:
+        words: 词列表
+        min_len: 子集最小长度
+
+    Returns:
+        所有可能的有序子集列表
+
+    Example:
+        words = ["川西", "秋季", "风光"]
+        结果:
+        - 长度1: ["川西"], ["秋季"], ["风光"]
+        - 长度2: ["川西", "秋季"], ["川西", "风光"], ["秋季", "风光"]
+        - 长度3: ["川西", "秋季", "风光"]
+        共 C(3,1) + C(3,2) + C(3,3) = 3 + 3 + 1 = 7种
+    """
+    from itertools import combinations
+
+    subsets = []
+    n = len(words)
+
+    # 遍历所有可能的长度(从min_len到n)
+    for r in range(min_len, n + 1):
+        # 生成长度为r的所有索引组合
+        for indices in combinations(range(n), r):
+            # 按照原始顺序提取词
+            subset = [words[i] for i in indices]
+            subsets.append(subset)
+
+    return subsets
+
+
+def generate_domain_combinations(segments: list[Segment], n_domains: int) -> list[DomainCombination]:
+    """
+    生成N域组合
+
+    步骤:
+    1. 从len(segments)个域中选择n_domains个域(组合,保持顺序)
+    2. 对每个选中的域,生成其words的所有有序子集
+    3. 计算笛卡尔积,生成所有可能的组合
+
+    Args:
+        segments: 语义片段列表
+        n_domains: 参与组合的域数量
+
+    Returns:
+        所有可能的N域组合列表
+
+    Example:
+        有4个域: [疑问标记, 核心动作, 修饰短语, 中心名词]
+        n_domains=2时,选择域的方式: C(4,2) = 6种
+
+        假设选中[核心动作, 中心名词]:
+        - 核心动作的words: ["获取"], 子集: ["获取"]
+        - 中心名词的words: ["风光", "摄影", "素材"], 子集: 7种
+        则该域选择下的组合数: 1 * 7 = 7种
+    """
+    from itertools import combinations, product
+
+    all_combinations = []
+    n = len(segments)
+
+    # 检查参数有效性
+    if n_domains > n or n_domains < 1:
+        return []
+
+    # 1. 选择n_domains个域(保持原始顺序)
+    for domain_indices in combinations(range(n), n_domains):
+        selected_segments = [segments[i] for i in domain_indices]
+
+        # 新增:如果所有域都只有1个词,跳过(单段落单词不组合)
+        if all(len(seg.words) == 1 for seg in selected_segments):
+            continue
+
+        # 2. 为每个选中的域生成其words的所有有序子集
+        domain_subsets = []
+        for seg in selected_segments:
+            if len(seg.words) == 0:
+                # 如果某个域没有词,跳过该域组合
+                domain_subsets = []
+                break
+            subsets = get_ordered_subsets(seg.words, min_len=1)
+            domain_subsets.append(subsets)
+
+        # 如果某个域没有词,跳过
+        if len(domain_subsets) != n_domains:
+            continue
+
+        # 3. 计算笛卡尔积
+        for word_combination in product(*domain_subsets):
+            # word_combination 是一个tuple,每个元素是一个词列表
+            # 例如: (["获取"], ["风光", "摄影"])
+
+            # 计算总词数
+            total_words = sum(len(words) for words in word_combination)
+
+            # 如果总词数<=1,跳过(组词必须大于1个词)
+            if total_words <= 1:
+                continue
+
+            # 将所有词连接成一个字符串
+            combined_text = "".join(["".join(words) for words in word_combination])
+
+            # 生成类型标签
+            type_labels = [selected_segments[i].type for i in range(n_domains)]
+            type_label = "[" + "+".join(type_labels) + "]"
+
+            # 创建DomainCombination对象
+            comb = DomainCombination(
+                text=combined_text,
+                domains=list(domain_indices),
+                type_label=type_label,
+                source_words=[list(words) for words in word_combination],  # 保存来源词
+                from_segments=[seg.text for seg in selected_segments]
+            )
+            all_combinations.append(comb)
+
+    return all_combinations
+
+
+def extract_words_from_segments(segments: list[Segment]) -> list[Q]:
+    """
+    从 segments 中提取所有 words,转换为 Q 对象列表
+
+    用于 Round 1 的输入:将 Round 0 的 words 转换为可用于请求SUG的 query 列表
+
+    Args:
+        segments: Round 0 的语义片段列表
+
+    Returns:
+        list[Q]: word 列表,每个 word 作为一个 Q 对象
+    """
+    q_list = []
+
+    for seg_idx, segment in enumerate(segments):
+        for word in segment.words:
+            # 从 segment.word_scores 获取该 word 的评分
+            word_score = segment.word_scores.get(word, 0.0)
+            word_reason = segment.word_reasons.get(word, "")
+
+            # 创建 Q 对象
+            q = Q(
+                text=word,
+                score_with_o=word_score,
+                reason=word_reason,
+                from_source="word",  # 标记来源为 word
+                type_label=f"[{segment.type}]",  # 保留域信息
+                domain_index=seg_idx,  # 添加域索引
+                domain_type=segment.type  # 添加域类型(如"中心名词"、"核心动作")
+            )
+            q_list.append(q)
+
+    return q_list
+
+
+# ============================================================================
+# v120 保留辅助函数
+# ============================================================================
+
+def calculate_final_score(
+    motivation_score: float,
+    category_score: float,
+    extension_score: float,
+    zero_reason: Optional[str],
+    extension_reason: str = ""
+) -> tuple[float, str]:
+    """
+    三维评估综合打分
+
+    实现动态权重分配:
+    - 情况1:标准情况 → 动机50% + 品类40% + 延伸词10%
+    - 情况2:原始问题无动机 → 品类70% + 延伸词30%
+    - 情况3:sug词条无动机 → 品类80% + 延伸词20%
+    - 情况4:无延伸词 → 动机70% + 品类30%
+    - 规则3:负分传导 → 核心维度严重负向时上限为0
+    - 规则4:完美匹配加成 → 双维度≥0.95时加成+0.10
+
+    Args:
+        motivation_score: 动机维度得分 -1~1
+        category_score: 品类维度得分 -1~1
+        extension_score: 延伸词得分 -1~1
+        zero_reason: 当motivation_score=0时的原因(可选)
+        extension_reason: 延伸词评估理由,用于判断是否无延伸词
+
+    Returns:
+        (最终得分, 规则说明)
+    """
+
+    # 情况2:原始问题无动作意图
+    if motivation_score == 0 and zero_reason == "原始问题无动机":
+        W1, W2, W3 = 0.0, 0.70, 0.30
+        base_score = category_score * W2 + extension_score * W3
+        rule_applied = "情况2:原始问题无动作意图,权重调整为 品类70% + 延伸词30%"
+
+    # 情况3:sug词条无动作意图(但原始问题有)
+    elif motivation_score == 0 and zero_reason == "sug词条无动机":
+        W1, W2, W3 = 0.0, 0.80, 0.20
+        base_score = category_score * W2 + extension_score * W3
+        rule_applied = "情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%"
+
+    # 情况4:无延伸词
+    elif extension_score == 0:
+        W1, W2, W3 = 0.70, 0.30, 0.0
+        base_score = motivation_score * W1 + category_score * W2
+        rule_applied = "情况4:无延伸词,权重调整为 动机70% + 品类30%"
+
+    else:
+        # 情况1:标准权重
+        W1, W2, W3 = 0.50, 0.40, 0.10
+        base_score = motivation_score * W1 + category_score * W2 + extension_score * W3
+        rule_applied = ""
+
+    # 规则4:完美匹配加成
+    if motivation_score >= 0.95 and category_score >= 0.95:
+        base_score += 0.10
+        rule_applied += (" + " if rule_applied else "") + "规则4:双维度完美匹配,加成+0.10"
+
+    # 规则3:负分传导
+    if motivation_score <= -0.5 or category_score <= -0.5:
+        base_score = min(base_score, 0)
+        rule_applied += (" + " if rule_applied else "") + "规则3:核心维度严重负向,上限=0"
+
+    # 边界处理
+    final_score = max(-1.0, min(1.0, base_score))
+
+    return final_score, rule_applied
+
+
+def calculate_final_score_v2(
+    motivation_score: float,
+    category_score: float
+) -> tuple[float, str]:
+    """
+    两维评估综合打分(v124新增 - 需求1)
+
+    用于Round 0分词评估和域内/域间评估,不含延伸词维度
+
+    基础权重:动机70% + 品类30%
+
+    应用规则:
+    - 规则A:动机高分保护机制
+      IF 动机维度得分 ≥ 0.8:
+         品类得分即使为0或轻微负向(-0.2~0)
+         → 最终得分应该不低于0.7
+      解释: 当目的高度一致时,品类的泛化不应导致"弱相关"
+
+    - 规则B:动机低分限制机制
+      IF 动机维度得分 ≤ 0.2:
+         无论品类得分多高
+         → 最终得分不高于0.5
+      解释: 目的不符时,品类匹配的价值有限
+
+    - 规则C:动机负向决定机制
+      IF 动机维度得分 < 0:
+         → 最终得分为0
+      解释: 动作意图冲突时,推荐具有误导性,不应为正相关
+
+    Args:
+        motivation_score: 动机维度得分 -1~1
+        category_score: 品类维度得分 -1~1
+
+    Returns:
+        (最终得分, 规则说明)
+    """
+
+    rule_applied = ""
+
+    # 规则C:动机负向决定机制
+    if motivation_score < 0:
+        final_score = 0.0
+        rule_applied = "规则C:动机负向,最终得分=0"
+        return final_score, rule_applied
+
+    # 基础加权计算: 动机70% + 品类30%
+    base_score = motivation_score * 0.7 + category_score * 0.3
+
+    # 规则A:动机高分保护机制
+    if motivation_score >= 0.8:
+        if base_score < 0.7:
+            final_score = 0.7
+            rule_applied = f"规则A:动机高分保护(动机{motivation_score:.2f}≥0.8),最终得分下限=0.7"
+        else:
+            final_score = base_score
+            rule_applied = f"规则A:动机高分保护生效(动机{motivation_score:.2f}≥0.8),实际得分{base_score:.2f}已≥0.7"
+
+    # 规则B:动机低分限制机制
+    elif motivation_score <= 0.2:
+        if base_score > 0.5:
+            final_score = 0.5
+            rule_applied = f"规则B:动机低分限制(动机{motivation_score:.2f}≤0.2),最终得分上限=0.5"
+        else:
+            final_score = base_score
+            rule_applied = f"规则B:动机低分限制生效(动机{motivation_score:.2f}≤0.2),实际得分{base_score:.2f}已≤0.5"
+
+    # 无规则触发
+    else:
+        final_score = base_score
+        rule_applied = ""
+
+    # 边界处理
+    final_score = max(-1.0, min(1.0, final_score))
+
+    return final_score, rule_applied
+
+
+def clean_json_string(text: str) -> str:
+    """清理JSON中的非法控制字符(保留 \t \n \r)"""
+    import re
+    # 移除除了 \t(09) \n(0A) \r(0D) 之外的所有控制字符
+    return re.sub(r'[\x00-\x08\x0B\x0C\x0E-\x1F]', '', text)
+
+
+def process_note_data(note: dict) -> Post:
+    """处理搜索接口返回的帖子数据"""
+    note_card = note.get("note_card", {})
+    image_list = note_card.get("image_list", [])
+    interact_info = note_card.get("interact_info", {})
+    user_info = note_card.get("user", {})
+
+    # ========== 调试日志 START ==========
+    note_id = note.get("id", "")
+    raw_title = note_card.get("display_title")  # 不提供默认值
+    raw_body = note_card.get("desc")
+    raw_type = note_card.get("type")
+
+    # 打印原始值类型和内容
+    print(f"\n[DEBUG] 处理帖子 {note_id}:")
+    print(f"  raw_title 类型: {type(raw_title).__name__}, 值: {repr(raw_title)}")
+    print(f"  raw_body 类型: {type(raw_body).__name__}, 值: {repr(raw_body)[:100] if raw_body else repr(raw_body)}")
+    print(f"  raw_type 类型: {type(raw_type).__name__}, 值: {repr(raw_type)}")
+
+    # 检查是否为 None
+    if raw_title is None:
+        print(f"  ⚠️  WARNING: display_title 是 None!")
+    if raw_body is None:
+        print(f"  ⚠️  WARNING: desc 是 None!")
+    if raw_type is None:
+        print(f"  ⚠️  WARNING: type 是 None!")
+    # ========== 调试日志 END ==========
+
+    # 提取图片URL - 使用新的字段名 image_url
+    images = []
+    for img in image_list:
+        if isinstance(img, dict):
+            # 尝试新字段名 image_url,如果不存在则尝试旧字段名 url_default
+            img_url = img.get("image_url") or img.get("url_default")
+            if img_url:
+                images.append(img_url)
+
+    # 判断类型
+    note_type = note_card.get("type", "normal")
+    video_url = ""
+    if note_type == "video":
+        video_info = note_card.get("video", {})
+        if isinstance(video_info, dict):
+            # 尝试获取视频URL
+            video_url = video_info.get("media", {}).get("stream", {}).get("h264", [{}])[0].get("master_url", "")
+
+    return Post(
+        note_id=note.get("id") or "",
+        title=note_card.get("display_title") or "",
+        body_text=note_card.get("desc") or "",
+        type=note_type,
+        images=images,
+        video=video_url,
+        interact_info={
+            "liked_count": interact_info.get("liked_count", 0),
+            "collected_count": interact_info.get("collected_count", 0),
+            "comment_count": interact_info.get("comment_count", 0),
+            "shared_count": interact_info.get("shared_count", 0)
+        },
+        note_url=f"https://www.xiaohongshu.com/explore/{note.get('id', '')}"
+    )
+
+
+async def evaluate_with_o(text: str, o: str, cache: dict[str, tuple[float, str]] | None = None) -> tuple[float, str]:
+    """评估文本与原始问题o的相关度
+
+    采用两阶段评估 + 代码计算规则:
+    1. 动机维度评估(权重70%)
+    2. 品类维度评估(权重30%)
+    3. 应用规则A/B/C调整得分
+
+    Args:
+        text: 待评估的文本
+        o: 原始问题
+        cache: 评估缓存(可选),用于避免重复评估
+
+    Returns:
+        tuple[float, str]: (最终相关度分数, 综合评估理由)
+    """
+    # 检查缓存
+    if cache is not None and text in cache:
+        cached_score, cached_reason = cache[text]
+        print(f"  ⚡ 缓存命中: {text} -> {cached_score:.2f}")
+        return cached_score, cached_reason
+
+    # 准备输入
+    eval_input = f"""
+<原始问题>
+{o}
+</原始问题>
+
+<平台sug词条>
+{text}
+</平台sug词条>
+
+请评估平台sug词条与原始问题的匹配度。
+"""
+
+    # 添加重试机制
+    max_retries = 2
+    last_error = None
+
+    for attempt in range(max_retries):
+        try:
+            # 并发调用三个评估器
+            motivation_task = Runner.run(motivation_evaluator, eval_input)
+            category_task = Runner.run(category_evaluator, eval_input)
+            extension_task = Runner.run(extension_word_evaluator, eval_input)
+
+            motivation_result, category_result, extension_result = await asyncio.gather(
+                motivation_task,
+                category_task,
+                extension_task
+            )
+
+            # 获取评估结果
+            motivation_eval: MotivationEvaluation = motivation_result.final_output
+            category_eval: CategoryEvaluation = category_result.final_output
+            extension_eval: ExtensionWordEvaluation = extension_result.final_output
+
+            # 提取得分
+            motivation_score = motivation_eval.动机维度得分
+            category_score = category_eval.品类维度得分
+            extension_score = extension_eval.延伸词得分
+            zero_reason = motivation_eval.得分为零的原因
+
+            # 应用规则计算最终得分
+            final_score, rule_applied = calculate_final_score(
+                motivation_score, category_score, extension_score, zero_reason,
+                extension_eval.简要说明延伸词维度相关度理由
+            )
+
+            # 组合评估理由
+            core_motivation = motivation_eval.原始问题核心动机提取.简要说明核心动机
+            motivation_reason = motivation_eval.简要说明动机维度相关度理由
+            category_reason = category_eval.简要说明品类维度相关度理由
+            extension_reason = extension_eval.简要说明延伸词维度相关度理由
+
+            combined_reason = (
+                f'【评估对象】词条"{text}" vs 原始问题"{o}"\n'
+                f"【核心动机】{core_motivation}\n"
+                f"【动机维度 {motivation_score:.2f}】{motivation_reason}\n"
+                f"【品类维度 {category_score:.2f}】{category_reason}\n"
+                f"【延伸词维度 {extension_score:.2f}】{extension_reason}\n"
+                f"【最终得分 {final_score:.2f}】"
+            )
+
+            # 添加规则说明
+            if rule_applied:
+                combined_reason += f"\n【规则说明】{rule_applied}"
+
+            # 存入缓存
+            if cache is not None:
+                cache[text] = (final_score, combined_reason)
+
+            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
+
+
+async def evaluate_with_o_round0(text: str, o: str, cache: dict[str, tuple[float, str]] | None = None) -> tuple[float, str]:
+    """Round 0专用评估函数(v124新增 - 需求1)
+
+    用于评估segment和word与原始问题的相关度
+    不含延伸词维度,使用Round 0专用Prompt和新评分逻辑
+
+    采用两维评估:
+    1. 动机维度评估(权重70%)
+    2. 品类维度评估(权重30%)
+    3. 应用规则A/B/C调整得分
+
+    Args:
+        text: 待评估的文本(segment或word)
+        o: 原始问题
+        cache: 评估缓存(可选),用于避免重复评估
+
+    Returns:
+        tuple[float, str]: (最终相关度分数, 综合评估理由)
+    """
+    # 检查缓存
+    cache_key = f"round0:{text}:{o}"  # 添加前缀以区分不同评估类型
+    if cache is not None and cache_key in cache:
+        cached_score, cached_reason = cache[cache_key]
+        print(f"  ⚡ Round0缓存命中: {text} -> {cached_score:.2f}")
+        return cached_score, cached_reason
+
+    # 准备输入
+    eval_input = f"""
+<原始问题>
+{o}
+</原始问题>
+
+<词条>
+{text}
+</词条>
+
+请评估词条与原始问题的匹配度。
+"""
+
+    # 添加重试机制
+    max_retries = 2
+    last_error = None
+
+    for attempt in range(max_retries):
+        try:
+            # 并发调用两个评估器(不含延伸词)
+            motivation_task = Runner.run(round0_motivation_evaluator, eval_input)
+            category_task = Runner.run(round0_category_evaluator, eval_input)
+
+            motivation_result, category_result = await asyncio.gather(
+                motivation_task,
+                category_task
+            )
+
+            # 获取评估结果
+            motivation_eval: MotivationEvaluation = motivation_result.final_output
+            category_eval: CategoryEvaluation = category_result.final_output
+
+            # 提取得分
+            motivation_score = motivation_eval.动机维度得分
+            category_score = category_eval.品类维度得分
+
+            # 应用新规则计算最终得分
+            final_score, rule_applied = calculate_final_score_v2(
+                motivation_score, category_score
+            )
+
+            # 组合评估理由
+            core_motivation = motivation_eval.原始问题核心动机提取.简要说明核心动机
+            motivation_reason = motivation_eval.简要说明动机维度相关度理由
+            category_reason = category_eval.简要说明品类维度相关度理由
+
+            combined_reason = (
+                f'【评估对象】词条"{text}" vs 原始问题"{o}"\n'
+                f"【核心动机】{core_motivation}\n"
+                f"【动机维度 {motivation_score:.2f}】{motivation_reason}\n"
+                f"【品类维度 {category_score:.2f}】{category_reason}\n"
+                f"【最终得分 {final_score:.2f}】"
+            )
+
+            # 添加规则说明
+            if rule_applied:
+                combined_reason += f"\n【规则说明】{rule_applied}"
+
+            # 存入缓存
+            if cache is not None:
+                cache[cache_key] = (final_score, combined_reason)
+
+            return final_score, combined_reason
+
+        except Exception as e:
+            last_error = e
+            error_msg = str(e)
+
+            if attempt < max_retries - 1:
+                print(f"  ⚠️  Round0评估失败 (尝试 {attempt+1}/{max_retries}): {error_msg[:150]}")
+                print(f"  正在重试...")
+                await asyncio.sleep(1)
+            else:
+                print(f"  ❌ Round0评估失败 (已达最大重试次数): {error_msg[:150]}")
+
+    # 所有重试失败后,返回默认值
+    fallback_reason = f"Round0评估失败(重试{max_retries}次): {str(last_error)[:200]}"
+    print(f"  使用默认值: score=0.0, reason={fallback_reason[:100]}...")
+    return 0.0, fallback_reason
+
+
+async def evaluate_within_scope(text: str, scope_text: str, cache: dict[str, tuple[float, str]] | None = None) -> tuple[float, str]:
+    """域内/域间专用评估函数(v124新增 - 需求2&3)
+
+    用于评估词条与作用域词条(单域或域组合)的相关度
+    不含延伸词维度,使用域内专用Prompt和新评分逻辑
+
+    采用两维评估:
+    1. 动机维度评估(权重70%)
+    2. 品类维度评估(权重30%)
+    3. 应用规则A/B/C调整得分
+
+    Args:
+        text: 待评估的词条
+        scope_text: 作用域词条(可以是单域词条或域组合词条)
+        cache: 评估缓存(可选),用于避免重复评估
+
+    Returns:
+        tuple[float, str]: (最终相关度分数, 综合评估理由)
+    """
+    # 检查缓存
+    cache_key = f"scope:{text}:{scope_text}"  # 添加前缀以区分不同评估类型
+    if cache is not None and cache_key in cache:
+        cached_score, cached_reason = cache[cache_key]
+        print(f"  ⚡ 域内缓存命中: {text} -> {cached_score:.2f}")
+        return cached_score, cached_reason
+
+    # 准备输入
+    eval_input = f"""
+<同一作用域词条>
+{scope_text}
+</同一作用域词条>
+
+<词条>
+{text}
+</词条>
+
+请评估词条与同一作用域词条的匹配度。
+"""
+
+    # 添加重试机制
+    max_retries = 2
+    last_error = None
+
+    for attempt in range(max_retries):
+        try:
+            # 并发调用两个评估器(不含延伸词)
+            motivation_task = Runner.run(scope_motivation_evaluator, eval_input)
+            category_task = Runner.run(scope_category_evaluator, eval_input)
+
+            motivation_result, category_result = await asyncio.gather(
+                motivation_task,
+                category_task
+            )
+
+            # 获取评估结果
+            motivation_eval: MotivationEvaluation = motivation_result.final_output
+            category_eval: CategoryEvaluation = category_result.final_output
+
+            # 提取得分
+            motivation_score = motivation_eval.动机维度得分
+            category_score = category_eval.品类维度得分
+
+            # 应用新规则计算最终得分
+            final_score, rule_applied = calculate_final_score_v2(
+                motivation_score, category_score
+            )
+
+            # 组合评估理由
+            core_motivation = motivation_eval.原始问题核心动机提取.简要说明核心动机
+            motivation_reason = motivation_eval.简要说明动机维度相关度理由
+            category_reason = category_eval.简要说明品类维度相关度理由
+
+            combined_reason = (
+                f'【评估对象】词条"{text}" vs 作用域词条"{scope_text}"\n'
+                f"【核心动机】{core_motivation}\n"
+                f"【动机维度 {motivation_score:.2f}】{motivation_reason}\n"
+                f"【品类维度 {category_score:.2f}】{category_reason}\n"
+                f"【最终得分 {final_score:.2f}】"
+            )
+
+            # 添加规则说明
+            if rule_applied:
+                combined_reason += f"\n【规则说明】{rule_applied}"
+
+            # 存入缓存
+            if cache is not None:
+                cache[cache_key] = (final_score, combined_reason)
+
+            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)
+            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
+
+
+# ============================================================================
+# v125 新增辅助函数(用于新评分逻辑)
+# ============================================================================
+
+def get_source_word_score(
+    word_text: str,
+    segment: Segment,
+    context: RunContext
+) -> float:
+    """
+    查找来源词的得分
+
+    查找顺序:
+    1. 先查 segment.word_scores (Round 0的单个词)
+    2. 再查 context.word_score_history (Round 1+的组合)
+
+    Args:
+        word_text: 词文本
+        segment: 该词所在的segment
+        context: 运行上下文
+
+    Returns:
+        词的得分,找不到返回0.0
+    """
+    # 优先查Round 0的词得分
+    if word_text in segment.word_scores:
+        return segment.word_scores[word_text]
+
+    # 其次查历史组合得分
+    if word_text in context.word_score_history:
+        return context.word_score_history[word_text]
+
+    # 都找不到
+    print(f"  ⚠️  警告: 未找到来源词得分: {word_text}")
+    return 0.0
+
+
+async def evaluate_domain_combination_round1(
+    comb: DomainCombination,
+    segments: list[Segment],
+    context: RunContext
+) -> tuple[float, str]:
+    """
+    Round 1 域内组合评估(新逻辑)
+
+    最终得分 = 品类得分 × 原始域得分
+
+    Args:
+        comb: 域内组合对象
+        segments: 所有segment列表
+        context: 运行上下文
+
+    Returns:
+        (最终得分, 评估理由)
+    """
+    # 获取所属segment
+    domain_idx = comb.domains[0] if comb.domains else 0
+    segment = segments[domain_idx] if 0 <= domain_idx < len(segments) else None
+
+    if not segment:
+        return 0.0, "错误: 无法找到所属segment"
+
+    # 拼接作用域文本
+    scope_text = segment.text
+
+    # 准备输入
+    eval_input = f"""
+<同一作用域词条>
+{scope_text}
+</同一作用域词条>
+
+<词条>
+{comb.text}
+</词条>
+
+请评估词条与同一作用域词条的匹配度。
+"""
+
+    # 只调用品类评估器
+    try:
+        category_result = await Runner.run(scope_category_evaluator, eval_input)
+        category_eval: CategoryEvaluation = category_result.final_output
+        category_score = category_eval.品类维度得分
+        category_reason = category_eval.简要说明品类维度相关度理由
+    except Exception as e:
+        print(f"  ❌ Round 1品类评估失败: {e}")
+        return 0.0, f"评估失败: {str(e)[:100]}"
+
+    # 计算最终得分
+    domain_score = segment.score_with_o
+    final_score = category_score * domain_score
+
+    # 组合评估理由
+    combined_reason = (
+        f'【Round 1 域内评估】\n'
+        f'【评估对象】组合"{comb.text}" vs 作用域"{scope_text}"\n'
+        f'【品类得分】{category_score:.2f} - {category_reason}\n'
+        f'【原始域得分】{domain_score:.2f}\n'
+        f'【计算公式】品类得分 × 域得分 = {category_score:.2f} × {domain_score:.2f}\n'
+        f'【最终得分】{final_score:.2f}'
+    )
+
+    return final_score, combined_reason
+
+
+async def evaluate_domain_combination_round2plus(
+    comb: DomainCombination,
+    segments: list[Segment],
+    context: RunContext
+) -> tuple[float, str]:
+    """
+    Round 2+ 域间组合评估(新逻辑 - 两步评估相乘)
+
+    步骤:
+    1. 计算全域组合得分 A: 全域组合 vs 原始query(动机+品类两维)
+    2. 计算部分组合得分 B: 部分组合 vs 全域组合(域内评估)
+    3. 最终得分 = A × B,截断到1.0
+
+    Args:
+        comb: 域间组合对象
+        segments: 所有segment列表
+        context: 运行上下文
+
+    Returns:
+        (最终得分, 评估理由)
+    """
+    # 全域组合文本 = 拼接所有参与组合的segments
+    full_domain_text = "".join(comb.from_segments)
+
+    # 步骤1: 计算全域组合得分 A
+    # 全域组合 vs 原始问题(动机+品类两维评估)
+    score_A, reason_A = await evaluate_with_o_round0(
+        full_domain_text,
+        context.o,  # 原始问题
+        context.evaluation_cache
+    )
+
+    # 步骤2: 计算部分组合得分 B
+    # 部分组合 vs 全域组合(域内评估)
+    score_B, reason_B = await evaluate_within_scope(
+        comb.text,          # 部分组合,如"获取川西"
+        full_domain_text,   # 全域组合,如"如何获取川西秋季特色"
+        context.evaluation_cache
+    )
+
+    # 步骤3: 计算最终得分
+    final_score = score_A * score_B
+    final_score = min(1.0, max(-1.0, final_score))  # 截断到[-1.0, 1.0]
+
+    # 组合评估理由
+    combined_reason = (
+        f'【Round 2+ 域间评估(两步评估相乘)】\n'
+        f'【评估对象】部分组合 "{comb.text}"\n'
+        f'\n'
+        f'【步骤1: 全域组合得分 A】\n'
+        f'  全域组合文本: "{full_domain_text}"\n'
+        f'  评估方式: 全域组合 vs 原始问题(动机+品类两维)\n'
+        f'  {reason_A}\n'
+        f'  得分A = {score_A:.2f}\n'
+        f'\n'
+        f'【步骤2: 部分组合得分 B】\n'
+        f'  部分组合文本: "{comb.text}"\n'
+        f'  评估方式: 部分组合 vs 全域组合(域内评估)\n'
+        f'  {reason_B}\n'
+        f'  得分B = {score_B:.2f}\n'
+        f'\n'
+        f'【最终得分】A × B = {score_A:.2f} × {score_B:.2f} = {score_A * score_B:.2f}\n'
+        f'【截断后】{final_score:.2f}'
+    )
+
+    return final_score, combined_reason
+
+
+# ============================================================================
+# 核心流程函数
+# ============================================================================
+
+async def initialize(o: str, context: RunContext) -> tuple[list[Seg], list[Word], list[Q], list[Seed]]:
+    """
+    初始化阶段
+
+    Returns:
+        (seg_list, word_list_1, q_list_1, seed_list)
+    """
+    print(f"\n{'='*60}")
+    print(f"初始化阶段")
+    print(f"{'='*60}")
+
+    # 1. 分词:原始问题(o) ->分词-> seg_list
+    print(f"\n[步骤1] 分词...")
+    result = await Runner.run(word_segmenter, o)
+    segmentation: WordSegmentation = result.final_output
+
+    seg_list = []
+    for word in segmentation.words:
+        seg_list.append(Seg(text=word, from_o=o))
+
+    print(f"分词结果: {[s.text for s in seg_list]}")
+    print(f"分词理由: {segmentation.reasoning}")
+
+    # 2. 分词评估:seg_list -> 每个seg与o进行评分(使用信号量限制并发数)
+    print(f"\n[步骤2] 评估每个分词与原始问题的相关度...")
+
+    MAX_CONCURRENT_SEG_EVALUATIONS = 10
+    seg_semaphore = asyncio.Semaphore(MAX_CONCURRENT_SEG_EVALUATIONS)
+
+    async def evaluate_seg(seg: Seg) -> Seg:
+        async with seg_semaphore:
+            # 初始化阶段的分词评估使用第一轮 prompt (round_num=1)
+            seg.score_with_o, seg.reason = await evaluate_with_o(seg.text, o, context.evaluation_cache, round_num=1)
+            return seg
+
+    if seg_list:
+        print(f"  开始评估 {len(seg_list)} 个分词(并发限制: {MAX_CONCURRENT_SEG_EVALUATIONS})...")
+        eval_tasks = [evaluate_seg(seg) for seg in seg_list]
+        await asyncio.gather(*eval_tasks)
+
+    for seg in seg_list:
+        print(f"  {seg.text}: {seg.score_with_o:.2f}")
+
+    # 3. 构建word_list_1: seg_list -> word_list_1(固定词库)
+    print(f"\n[步骤3] 构建word_list_1(固定词库)...")
+    word_list_1 = []
+    for seg in seg_list:
+        word_list_1.append(Word(
+            text=seg.text,
+            score_with_o=seg.score_with_o,
+            from_o=o
+        ))
+    print(f"word_list_1(固定): {[w.text for w in word_list_1]}")
+
+    # 4. 构建q_list_1:seg_list 作为 q_list_1
+    print(f"\n[步骤4] 构建q_list_1...")
+    q_list_1 = []
+    for seg in seg_list:
+        q_list_1.append(Q(
+            text=seg.text,
+            score_with_o=seg.score_with_o,
+            reason=seg.reason,
+            from_source="seg"
+        ))
+    print(f"q_list_1: {[q.text for q in q_list_1]}")
+
+    # 5. 构建seed_list: seg_list -> seed_list
+    print(f"\n[步骤5] 构建seed_list...")
+    seed_list = []
+    for seg in seg_list:
+        seed_list.append(Seed(
+            text=seg.text,
+            added_words=[],
+            from_type="seg",
+            score_with_o=seg.score_with_o
+        ))
+    print(f"seed_list: {[s.text for s in seed_list]}")
+
+    return seg_list, word_list_1, q_list_1, seed_list
+
+
+async def run_round(
+    round_num: int,
+    q_list: list[Q],
+    word_list_1: list[Word],
+    seed_list: list[Seed],
+    o: str,
+    context: RunContext,
+    xiaohongshu_api: XiaohongshuSearchRecommendations,
+    xiaohongshu_search: XiaohongshuSearch,
+    sug_threshold: float = 0.7
+) -> tuple[list[Q], list[Seed], list[Search]]:
+    """
+    运行一轮
+
+    Args:
+        round_num: 轮次编号
+        q_list: 当前轮的q列表
+        word_list_1: 固定的词库(第0轮分词结果)
+        seed_list: 当前的seed列表
+        o: 原始问题
+        context: 运行上下文
+        xiaohongshu_api: 建议词API
+        xiaohongshu_search: 搜索API
+        sug_threshold: suggestion的阈值
+
+    Returns:
+        (q_list_next, seed_list_next, search_list)
+    """
+    print(f"\n{'='*60}")
+    print(f"第{round_num}轮")
+    print(f"{'='*60}")
+
+    round_data = {
+        "round_num": round_num,
+        "input_q_list": [{"text": q.text, "score": q.score_with_o, "type": "query"} for q in q_list],
+        "input_word_list_1_size": len(word_list_1),
+        "input_seed_list_size": len(seed_list)
+    }
+
+    # 1. 请求sug:q_list -> 每个q请求sug接口 -> sug_list_list
+    print(f"\n[步骤1] 为每个q请求建议词...")
+    sug_list_list = []  # list of list
+    for q in q_list:
+        print(f"\n  处理q: {q.text}")
+        suggestions = xiaohongshu_api.get_recommendations(keyword=q.text)
+
+        q_sug_list = []
+        if suggestions:
+            print(f"    获取到 {len(suggestions)} 个建议词")
+            for sug_text in suggestions:
+                sug = Sug(
+                    text=sug_text,
+                    from_q=QFromQ(text=q.text, score_with_o=q.score_with_o)
+                )
+                q_sug_list.append(sug)
+        else:
+            print(f"    未获取到建议词")
+
+        sug_list_list.append(q_sug_list)
+
+    # 2. sug评估:sug_list_list -> 每个sug与o进行评分(并发)
+    print(f"\n[步骤2] 评估每个建议词与原始问题的相关度...")
+
+    # 2.1 收集所有需要评估的sug,并记录它们所属的q
+    all_sugs = []
+    sug_to_q_map = {}  # 记录每个sug属于哪个q
+    for i, q_sug_list in enumerate(sug_list_list):
+        if q_sug_list:
+            q_text = q_list[i].text
+            for sug in q_sug_list:
+                all_sugs.append(sug)
+                sug_to_q_map[id(sug)] = q_text
+
+    # 2.2 并发评估所有sug(使用信号量限制并发数)
+    # 每个 evaluate_sug 内部会并发调用 2 个 LLM,所以这里限制为 5,实际并发 LLM 请求为 10
+    MAX_CONCURRENT_EVALUATIONS = 5
+    semaphore = asyncio.Semaphore(MAX_CONCURRENT_EVALUATIONS)
+
+    async def evaluate_sug(sug: Sug) -> Sug:
+        async with semaphore:  # 限制并发数
+            # 根据轮次选择 prompt: 第一轮使用 round1 prompt,后续使用标准 prompt
+            sug.score_with_o, sug.reason = await evaluate_with_o(sug.text, o, context.evaluation_cache, round_num=round_num)
+            return sug
+
+    if all_sugs:
+        print(f"  开始评估 {len(all_sugs)} 个建议词(并发限制: {MAX_CONCURRENT_EVALUATIONS})...")
+        eval_tasks = [evaluate_sug(sug) for sug in all_sugs]
+        await asyncio.gather(*eval_tasks)
+
+    # 2.3 打印结果并组织到sug_details
+    sug_details = {}  # 保存每个Q对应的sug列表
+    for i, q_sug_list in enumerate(sug_list_list):
+        if q_sug_list:
+            q_text = q_list[i].text
+            print(f"\n  来自q '{q_text}' 的建议词:")
+            sug_details[q_text] = []
+            for sug in q_sug_list:
+                print(f"    {sug.text}: {sug.score_with_o:.2f}")
+                # 保存到sug_details
+                sug_details[q_text].append({
+                    "text": sug.text,
+                    "score": sug.score_with_o,
+                    "reason": sug.reason,
+                    "type": "sug"
+                })
+
+    # 2.4 剪枝判断(已禁用 - 保留所有分支)
+    pruned_query_texts = set()
+    if False:  # 原: if round_num >= 2:  # 剪枝功能已禁用,保留代码以便后续调整
+        print(f"\n[剪枝判断] 第{round_num}轮开始应用剪枝策略...")
+        for i, q in enumerate(q_list):
+            q_sug_list = sug_list_list[i]
+
+            if len(q_sug_list) == 0:
+                continue  # 没有sug则不剪枝
+
+            # 剪枝条件1: 所有sug分数都低于query分数
+            all_lower_than_query = all(sug.score_with_o < q.score_with_o for sug in q_sug_list)
+            # 剪枝条件2: 所有sug分数都低于0.5
+            all_below_threshold = all(sug.score_with_o < 0.5 for sug in q_sug_list)
+
+            if all_lower_than_query and all_below_threshold:
+                pruned_query_texts.add(q.text)
+                max_sug_score = max(sug.score_with_o for sug in q_sug_list)
+                print(f"  🔪 剪枝: {q.text} (query分数:{q.score_with_o:.2f}, sug最高分:{max_sug_score:.2f}, 全部<0.5)")
+
+        if pruned_query_texts:
+            print(f"  本轮共剪枝 {len(pruned_query_texts)} 个query")
+        else:
+            print(f"  本轮无query被剪枝")
+    else:
+        print(f"\n[剪枝判断] 剪枝功能已禁用,保留所有分支")
+
+    # 3. search_list构建
+    print(f"\n[步骤3] 构建search_list(阈值>{sug_threshold})...")
+    search_list = []
+    high_score_sugs = [sug for sug in all_sugs if sug.score_with_o > sug_threshold]
+
+    if high_score_sugs:
+        print(f"  找到 {len(high_score_sugs)} 个高分建议词")
+
+        # 并发搜索
+        async def search_for_sug(sug: Sug) -> Search:
+            print(f"    搜索: {sug.text}")
+            try:
+                search_result = xiaohongshu_search.search(keyword=sug.text)
+                result_str = search_result.get("result", "{}")
+                if isinstance(result_str, str):
+                    result_data = json.loads(result_str)
+                else:
+                    result_data = result_str
+
+                notes = result_data.get("data", {}).get("data", [])
+                post_list = []
+                for note in notes[:10]:  # 只取前10个
+                    post = process_note_data(note)
+                    post_list.append(post)
+
+                print(f"      → 找到 {len(post_list)} 个帖子")
+
+                return Search(
+                    text=sug.text,
+                    score_with_o=sug.score_with_o,
+                    from_q=sug.from_q,
+                    post_list=post_list
+                )
+            except Exception as e:
+                print(f"      ✗ 搜索失败: {e}")
+                return Search(
+                    text=sug.text,
+                    score_with_o=sug.score_with_o,
+                    from_q=sug.from_q,
+                    post_list=[]
+                )
+
+        search_tasks = [search_for_sug(sug) for sug in high_score_sugs]
+        search_list = await asyncio.gather(*search_tasks)
+    else:
+        print(f"  没有高分建议词,search_list为空")
+
+    # 4. 构建q_list_next
+    print(f"\n[步骤4] 构建q_list_next...")
+    q_list_next = []
+    existing_q_texts = set()  # 用于去重
+    add_word_details = {}  # 保存每个seed对应的组合词列表
+    all_seed_combinations = []  # 保存本轮所有seed的组合词(用于后续构建seed_list_next)
+
+    # 4.1 对于seed_list中的每个seed,从word_list_1中选词组合,产生Top 5
+    print(f"\n  4.1 为每个seed加词(产生Top 5组合)...")
+    for seed in seed_list:
+        print(f"\n    处理seed: {seed.text}")
+
+        # 剪枝检查:跳过被剪枝的seed
+        if seed.text in pruned_query_texts:
+            print(f"      ⊗ 跳过被剪枝的seed: {seed.text}")
+            continue
+
+        # 从固定词库word_list_1筛选候选词
+        candidate_words = []
+        for word in word_list_1:
+            # 检查词是否已在seed中
+            if word.text in seed.text:
+                continue
+            # 检查词是否已被添加过
+            if word.text in seed.added_words:
+                continue
+            candidate_words.append(word)
+
+        if not candidate_words:
+            print(f"      没有可用的候选词")
+            continue
+
+        print(f"      候选词数量: {len(candidate_words)}")
+
+        # 调用Agent一次性选择并组合Top 5(添加重试机制)
+        candidate_words_text = ', '.join([w.text for w in candidate_words])
+        selection_input = f"""
+<原始问题>
+{o}
+</原始问题>
+
+<当前Seed>
+{seed.text}
+</当前Seed>
+
+<候选词列表>
+{candidate_words_text}
+</候选词列表>
+
+请从候选词列表中选择最多5个最合适的词,分别与当前seed组合成新的query。
+"""
+
+        # 重试机制
+        max_retries = 2
+        selection_result = None
+        for attempt in range(max_retries):
+            try:
+                result = await Runner.run(word_selector, selection_input)
+                selection_result = result.final_output
+                break  # 成功则跳出
+            except Exception as e:
+                error_msg = str(e)
+                if attempt < max_retries - 1:
+                    print(f"      ⚠️  选词失败 (尝试 {attempt+1}/{max_retries}): {error_msg[:100]}")
+                    await asyncio.sleep(1)
+                else:
+                    print(f"      ❌ 选词失败,跳过该seed: {error_msg[:100]}")
+                    break
+
+        if selection_result is None:
+            print(f"      跳过seed: {seed.text}")
+            continue
+
+        print(f"      Agent选择了 {len(selection_result.combinations)} 个组合")
+        print(f"      整体选择思路: {selection_result.overall_reasoning}")
+
+        # 并发评估所有组合的相关度
+        async def evaluate_combination(comb: WordCombination) -> dict:
+            combined = comb.combined_query
+
+            # 验证:组合结果必须包含完整的seed和word
+            # 检查是否包含seed的所有字符
+            seed_chars_in_combined = all(char in combined for char in seed.text)
+            # 检查是否包含word的所有字符
+            word_chars_in_combined = all(char in combined for char in comb.selected_word)
+
+            if not seed_chars_in_combined or not word_chars_in_combined:
+                print(f"        ⚠️  警告:组合不完整")
+                print(f"          Seed: {seed.text}")
+                print(f"          Word: {comb.selected_word}")
+                print(f"          组合: {combined}")
+                print(f"          包含完整seed? {seed_chars_in_combined}")
+                print(f"          包含完整word? {word_chars_in_combined}")
+                # 返回极低分数,让这个组合不会被选中
+                return {
+                    'word': comb.selected_word,
+                    'query': combined,
+                    'score': -1.0,  # 极低分数
+                    'reason': f"组合不完整:缺少seed或word的部分内容",
+                    'reasoning': comb.reasoning
+                }
+
+            # 正常评估,根据轮次选择 prompt
+            score, reason = await evaluate_with_o(combined, o, context.evaluation_cache, round_num=round_num)
+            return {
+                'word': comb.selected_word,
+                'query': combined,
+                'score': score,
+                'reason': reason,
+                'reasoning': comb.reasoning
+            }
+
+        eval_tasks = [evaluate_combination(comb) for comb in selection_result.combinations]
+        top_5 = await asyncio.gather(*eval_tasks)
+
+        print(f"      评估完成,得到 {len(top_5)} 个组合")
+
+        # 将Top 5全部加入q_list_next(去重检查 + 得分过滤)
+        for comb in top_5:
+            # 得分过滤:组合词必须比种子提升至少REQUIRED_SCORE_GAIN才能加入下一轮
+            if comb['score'] < seed.score_with_o + REQUIRED_SCORE_GAIN:
+                print(f"        ⊗ 跳过低分: {comb['query']} (分数{comb['score']:.2f} < 种子{seed.score_with_o:.2f} + {REQUIRED_SCORE_GAIN:.2f})")
+                continue
+
+            # 去重检查
+            if comb['query'] in existing_q_texts:
+                print(f"        ⊗ 跳过重复: {comb['query']}")
+                continue
+
+            print(f"        ✓ {comb['query']} (分数: {comb['score']:.2f} > 种子: {seed.score_with_o:.2f})")
+
+            new_q = Q(
+                text=comb['query'],
+                score_with_o=comb['score'],
+                reason=comb['reason'],
+                from_source="add"
+            )
+            q_list_next.append(new_q)
+            existing_q_texts.add(comb['query'])  # 记录到去重集合
+
+            # 记录已添加的词
+            seed.added_words.append(comb['word'])
+
+        # 保存到add_word_details
+        add_word_details[seed.text] = [
+            {
+                "text": comb['query'],
+                "score": comb['score'],
+                "reason": comb['reason'],
+                "selected_word": comb['word'],
+                "seed_score": seed.score_with_o,  # 添加原始种子的得分
+                "type": "add"
+            }
+            for comb in top_5
+        ]
+
+        # 保存到all_seed_combinations(用于构建seed_list_next)
+        # 附加seed_score,用于后续过滤
+        for comb in top_5:
+            comb['seed_score'] = seed.score_with_o
+        all_seed_combinations.extend(top_5)
+
+    # 4.2 对于sug_list_list中,每个sug大于来自的query分数,加到q_list_next(去重检查)
+    print(f"\n  4.2 将高分sug加入q_list_next...")
+    for sug in all_sugs:
+        # 剪枝检查:跳过来自被剪枝query的sug
+        if sug.from_q and sug.from_q.text in pruned_query_texts:
+            print(f"    ⊗ 跳过来自被剪枝query的sug: {sug.text} (来源: {sug.from_q.text})")
+            continue
+
+        # sug必须比来源query提升至少REQUIRED_SCORE_GAIN才能加入下一轮
+        if sug.from_q and sug.score_with_o >= sug.from_q.score_with_o + REQUIRED_SCORE_GAIN:
+            # 去重检查
+            if sug.text in existing_q_texts:
+                print(f"    ⊗ 跳过重复: {sug.text}")
+                continue
+
+            new_q = Q(
+                text=sug.text,
+                score_with_o=sug.score_with_o,
+                reason=sug.reason,
+                from_source="sug"
+            )
+            q_list_next.append(new_q)
+            existing_q_texts.add(sug.text)  # 记录到去重集合
+            print(f"    ✓ {sug.text} (分数: {sug.score_with_o:.2f} >= 来源query: {sug.from_q.score_with_o:.2f} + {REQUIRED_SCORE_GAIN:.2f})")
+
+    # 5. 构建seed_list_next(关键修改:不保留上一轮的seed)
+    print(f"\n[步骤5] 构建seed_list_next(不保留上轮seed)...")
+    seed_list_next = []
+    existing_seed_texts = set()
+
+    # 5.1 加入本轮所有组合词(只加入得分提升的)
+    print(f"  5.1 加入本轮所有组合词(得分过滤)...")
+    for comb in all_seed_combinations:
+        # 得分过滤:组合词必须比种子提升至少REQUIRED_SCORE_GAIN才作为下一轮种子
+        seed_score = comb.get('seed_score', 0)
+        if comb['score'] < seed_score + REQUIRED_SCORE_GAIN:
+            print(f"    ⊗ 跳过低分: {comb['query']} (分数{comb['score']:.2f} < 种子{seed_score:.2f} + {REQUIRED_SCORE_GAIN:.2f})")
+            continue
+
+        if comb['query'] not in existing_seed_texts:
+            new_seed = Seed(
+                text=comb['query'],
+                added_words=[],  # 新seed的added_words清空
+                from_type="add",
+                score_with_o=comb['score']
+            )
+            seed_list_next.append(new_seed)
+            existing_seed_texts.add(comb['query'])
+            print(f"    ✓ {comb['query']} (分数: {comb['score']:.2f} >= 种子: {seed_score:.2f} + {REQUIRED_SCORE_GAIN:.2f})")
+
+    # 5.2 加入高分sug
+    print(f"  5.2 加入高分sug...")
+    for sug in all_sugs:
+        # 剪枝检查:跳过来自被剪枝query的sug
+        if sug.from_q and sug.from_q.text in pruned_query_texts:
+            continue
+
+        # sug必须比来源query提升至少REQUIRED_SCORE_GAIN才作为下一轮种子
+        if sug.from_q and sug.score_with_o >= sug.from_q.score_with_o + REQUIRED_SCORE_GAIN and sug.text not in existing_seed_texts:
+            new_seed = Seed(
+                text=sug.text,
+                added_words=[],
+                from_type="sug",
+                score_with_o=sug.score_with_o
+            )
+            seed_list_next.append(new_seed)
+            existing_seed_texts.add(sug.text)
+            print(f"    ✓ {sug.text} (分数: {sug.score_with_o:.2f} >= 来源query: {sug.from_q.score_with_o:.2f} + {REQUIRED_SCORE_GAIN:.2f})")
+
+    # 序列化搜索结果数据(包含帖子详情)
+    search_results_data = []
+    for search in search_list:
+        search_results_data.append({
+            "text": search.text,
+            "score_with_o": search.score_with_o,
+            "post_list": [
+                {
+                    "note_id": post.note_id,
+                    "note_url": post.note_url,
+                    "title": post.title,
+                    "body_text": post.body_text,
+                    "images": post.images,
+                    "interact_info": post.interact_info
+                }
+                for post in search.post_list
+            ]
+        })
+
+    # 记录本轮数据
+    round_data.update({
+        "sug_count": len(all_sugs),
+        "high_score_sug_count": len(high_score_sugs),
+        "search_count": len(search_list),
+        "total_posts": sum(len(s.post_list) for s in search_list),
+        "q_list_next_size": len(q_list_next),
+        "seed_list_next_size": len(seed_list_next),
+        "total_combinations": len(all_seed_combinations),
+        "pruned_query_count": len(pruned_query_texts),
+        "pruned_queries": list(pruned_query_texts),
+        "output_q_list": [{"text": q.text, "score": q.score_with_o, "reason": q.reason, "from": q.from_source, "type": "query"} for q in q_list_next],
+        "seed_list_next": [{"text": seed.text, "from": seed.from_type, "score": seed.score_with_o} for seed in seed_list_next],
+        "sug_details": sug_details,
+        "add_word_details": add_word_details,
+        "search_results": search_results_data
+    })
+    context.rounds.append(round_data)
+
+    print(f"\n本轮总结:")
+    print(f"  建议词数量: {len(all_sugs)}")
+    print(f"  高分建议词: {len(high_score_sugs)}")
+    print(f"  搜索数量: {len(search_list)}")
+    print(f"  帖子总数: {sum(len(s.post_list) for s in search_list)}")
+    print(f"  组合词数量: {len(all_seed_combinations)}")
+    print(f"  下轮q数量: {len(q_list_next)}")
+    print(f"  下轮seed数量: {len(seed_list_next)}")
+
+    return q_list_next, seed_list_next, search_list
+
+
+async def iterative_loop(
+    context: RunContext,
+    max_rounds: int = 2,
+    sug_threshold: float = 0.7
+):
+    """主迭代循环"""
+
+    print(f"\n{'='*60}")
+    print(f"开始迭代循环")
+    print(f"最大轮数: {max_rounds}")
+    print(f"sug阈值: {sug_threshold}")
+    print(f"{'='*60}")
+
+    # 初始化
+    seg_list, word_list_1, q_list, seed_list = await initialize(context.o, context)
+
+    # API实例
+    xiaohongshu_api = XiaohongshuSearchRecommendations()
+    xiaohongshu_search = XiaohongshuSearch()
+
+    # 保存初始化数据
+    context.rounds.append({
+        "round_num": 0,
+        "type": "initialization",
+        "seg_list": [{"text": s.text, "score": s.score_with_o, "reason": s.reason, "type": "seg"} for s in seg_list],
+        "word_list_1": [{"text": w.text, "score": w.score_with_o} for w in word_list_1],
+        "q_list_1": [{"text": q.text, "score": q.score_with_o, "reason": q.reason, "type": "query"} for q in q_list],
+        "seed_list": [{"text": s.text, "from_type": s.from_type, "score": s.score_with_o, "type": "seed"} for s in seed_list]
+    })
+
+    # 收集所有搜索结果
+    all_search_list = []
+
+    # 迭代
+    round_num = 1
+    while q_list and round_num <= max_rounds:
+        q_list, seed_list, search_list = await run_round(
+            round_num=round_num,
+            q_list=q_list,
+            word_list_1=word_list_1,  # 传递固定词库
+            seed_list=seed_list,
+            o=context.o,
+            context=context,
+            xiaohongshu_api=xiaohongshu_api,
+            xiaohongshu_search=xiaohongshu_search,
+            sug_threshold=sug_threshold
+        )
+
+        all_search_list.extend(search_list)
+        round_num += 1
+
+    print(f"\n{'='*60}")
+    print(f"迭代完成")
+    print(f"  总轮数: {round_num - 1}")
+    print(f"  总搜索次数: {len(all_search_list)}")
+    print(f"  总帖子数: {sum(len(s.post_list) for s in all_search_list)}")
+    print(f"{'='*60}")
+
+    return all_search_list
+
+
+# ============================================================================
+# v121 新架构核心流程函数
+# ============================================================================
+
+async def initialize_v2(o: str, context: RunContext) -> list[Segment]:
+    """
+    v121 Round 0 初始化阶段
+
+    流程:
+    1. 语义分段: 调用 semantic_segmenter 将原始问题拆分成语义片段
+    2. 拆词: 对每个segment调用 word_segmenter 进行拆词
+    3. 评估: 对每个segment和词进行评估
+    4. 不进行组合(Round 0只分段和拆词)
+
+    Returns:
+        语义片段列表 (Segment)
+    """
+    print(f"\n{'='*60}")
+    print(f"Round 0: 初始化阶段(语义分段 + 拆词)")
+    print(f"{'='*60}")
+
+    # 1. 语义分段
+    print(f"\n[步骤1] 语义分段...")
+    result = await Runner.run(semantic_segmenter, o)
+    segmentation: SemanticSegmentation = result.final_output
+
+    print(f"语义分段结果: {len(segmentation.segments)} 个片段")
+    print(f"整体分段思路: {segmentation.overall_reasoning}")
+
+    segment_list = []
+    for seg_item in segmentation.segments:
+        segment = Segment(
+            text=seg_item.segment_text,
+            type=seg_item.segment_type,
+            from_o=o
+        )
+        segment_list.append(segment)
+        print(f"  - [{segment.type}] {segment.text}")
+
+    # 2. 对每个segment拆词并评估
+    print(f"\n[步骤2] 对每个segment拆词并评估...")
+
+    MAX_CONCURRENT_EVALUATIONS = 5
+    semaphore = asyncio.Semaphore(MAX_CONCURRENT_EVALUATIONS)
+
+    async def process_segment(segment: Segment) -> Segment:
+        """处理单个segment: 拆词 + 评估segment + 评估词"""
+        async with semaphore:
+            # 2.1 拆词
+            word_result = await Runner.run(word_segmenter, segment.text)
+            word_segmentation: WordSegmentation = word_result.final_output
+            segment.words = word_segmentation.words
+
+            # 2.2 评估segment与原始问题的相关度(使用Round 0专用评估)
+            segment.score_with_o, segment.reason = await evaluate_with_o_round0(
+                segment.text, o, context.evaluation_cache
+            )
+
+            # 2.3 评估每个词与原始问题的相关度(使用Round 0专用评估)
+            word_eval_tasks = []
+            for word in segment.words:
+                async def eval_word(w: str) -> tuple[str, float, str]:
+                    score, reason = await evaluate_with_o_round0(w, o, context.evaluation_cache)
+                    return w, score, reason
+                word_eval_tasks.append(eval_word(word))
+
+            word_results = await asyncio.gather(*word_eval_tasks)
+            for word, score, reason in word_results:
+                segment.word_scores[word] = score
+                segment.word_reasons[word] = reason
+
+            return segment
+
+    if segment_list:
+        print(f"  开始处理 {len(segment_list)} 个segment(并发限制: {MAX_CONCURRENT_EVALUATIONS})...")
+        process_tasks = [process_segment(seg) for seg in segment_list]
+        await asyncio.gather(*process_tasks)
+
+    # 打印步骤1结果
+    print(f"\n[步骤1: 分段及拆词 结果]")
+    for segment in segment_list:
+        print(f"  [{segment.type}] {segment.text} (分数: {segment.score_with_o:.2f})")
+        print(f"    拆词: {segment.words}")
+        for word in segment.words:
+            score = segment.word_scores.get(word, 0.0)
+            print(f"      - {word}: {score:.2f}")
+
+    # 保存到context(保留旧格式以兼容)
+    context.segments = [
+        {
+            "text": seg.text,
+            "type": seg.type,
+            "score": seg.score_with_o,
+            "reason": seg.reason,
+            "words": seg.words,
+            "word_scores": seg.word_scores,
+            "word_reasons": seg.word_reasons
+        }
+        for seg in segment_list
+    ]
+
+    # 保存 Round 0 到 context.rounds(新格式用于可视化)
+    context.rounds.append({
+        "round_num": 0,
+        "type": "initialization",
+        "segments": [
+            {
+                "text": seg.text,
+                "type": seg.type,
+                "domain_index": idx,
+                "score": seg.score_with_o,
+                "reason": seg.reason,
+                "words": [
+                    {
+                        "text": word,
+                        "score": seg.word_scores.get(word, 0.0),
+                        "reason": seg.word_reasons.get(word, "")
+                    }
+                    for word in seg.words
+                ]
+            }
+            for idx, seg in enumerate(segment_list)
+        ]
+    })
+
+    # 🆕 存储Round 0的所有word得分到历史记录
+    print(f"\n[存储Round 0词得分到历史记录]")
+    for segment in segment_list:
+        for word, score in segment.word_scores.items():
+            context.word_score_history[word] = score
+            print(f"  {word}: {score:.2f}")
+
+    print(f"\n[Round 0 完成]")
+    print(f"  分段数: {len(segment_list)}")
+    total_words = sum(len(seg.words) for seg in segment_list)
+    print(f"  总词数: {total_words}")
+
+    return segment_list
+
+
+async def run_round_v2(
+    round_num: int,
+    query_input: list[Q],
+    segments: list[Segment],
+    o: str,
+    context: RunContext,
+    xiaohongshu_api: XiaohongshuSearchRecommendations,
+    xiaohongshu_search: XiaohongshuSearch,
+    sug_threshold: float = 0.7
+) -> tuple[list[Q], list[Search]]:
+    """
+    v121 Round N 执行
+
+    正确的流程顺序:
+    1. 为 query_input 请求SUG
+    2. 评估SUG
+    3. 高分SUG搜索
+    4. N域组合(从segments生成)
+    5. 评估组合
+    6. 生成 q_list_next(组合 + 高分SUG)
+
+    Args:
+        round_num: 轮次编号 (1-4)
+        query_input: 本轮的输入query列表(Round 1是words,Round 2+是上轮输出)
+        segments: 语义片段列表(用于组合)
+        o: 原始问题
+        context: 运行上下文
+        xiaohongshu_api: 建议词API
+        xiaohongshu_search: 搜索API
+        sug_threshold: SUG搜索阈值
+
+    Returns:
+        (q_list_next, search_list)
+    """
+    print(f"\n{'='*60}")
+    print(f"Round {round_num}: {round_num}域组合")
+    print(f"{'='*60}")
+
+    round_data = {
+        "round_num": round_num,
+        "n_domains": round_num,
+        "input_query_count": len(query_input)
+    }
+
+    MAX_CONCURRENT_EVALUATIONS = 5
+    semaphore = asyncio.Semaphore(MAX_CONCURRENT_EVALUATIONS)
+
+    # 步骤1: 为 query_input 请求SUG
+    print(f"\n[步骤1] 为{len(query_input)}个输入query请求SUG...")
+    all_sugs = []
+    sug_details = {}
+
+    for q in query_input:
+        suggestions = xiaohongshu_api.get_recommendations(keyword=q.text)
+        if suggestions:
+            print(f"  {q.text}: 获取到 {len(suggestions)} 个SUG")
+            for sug_text in suggestions:
+                sug = Sug(
+                    text=sug_text,
+                    from_q=QFromQ(text=q.text, score_with_o=q.score_with_o)
+                )
+                all_sugs.append(sug)
+        else:
+            print(f"  {q.text}: 未获取到SUG")
+
+    print(f"  共获取 {len(all_sugs)} 个SUG")
+
+    # 步骤2: 评估SUG
+    if len(all_sugs) > 0:
+        print(f"\n[步骤2] 评估{len(all_sugs)}个SUG...")
+
+        async def evaluate_sug(sug: Sug) -> Sug:
+            async with semaphore:
+                sug.score_with_o, sug.reason = await evaluate_with_o(
+                    sug.text, o, context.evaluation_cache
+                )
+                return sug
+
+        eval_tasks = [evaluate_sug(sug) for sug in all_sugs]
+        await asyncio.gather(*eval_tasks)
+
+        # 打印结果
+        for sug in all_sugs:
+            print(f"    {sug.text}: {sug.score_with_o:.2f}")
+            if sug.from_q:
+                if sug.from_q.text not in sug_details:
+                    sug_details[sug.from_q.text] = []
+                sug_details[sug.from_q.text].append({
+                    "text": sug.text,
+                    "score": sug.score_with_o,
+                    "reason": sug.reason,
+                    "type": "sug"
+                })
+
+    # 步骤3: 搜索高分SUG
+    print(f"\n[步骤3] 搜索高分SUG(阈值 > {sug_threshold})...")
+    high_score_sugs = [sug for sug in all_sugs if sug.score_with_o > sug_threshold]
+    print(f"  找到 {len(high_score_sugs)} 个高分SUG")
+
+    search_list = []
+    if len(high_score_sugs) > 0:
+        async def search_for_sug(sug: Sug) -> Search:
+            print(f"    搜索: {sug.text}")
+            try:
+                search_result = xiaohongshu_search.search(keyword=sug.text)
+                result_str = search_result.get("result", "{}")
+                if isinstance(result_str, str):
+                    result_data = json.loads(result_str)
+                else:
+                    result_data = result_str
+
+                notes = result_data.get("data", {}).get("data", [])
+                post_list = []
+                for note in notes[:10]:
+                    post = process_note_data(note)
+                    post_list.append(post)
+
+                print(f"      → 找到 {len(post_list)} 个帖子")
+
+                return Search(
+                    text=sug.text,
+                    score_with_o=sug.score_with_o,
+                    from_q=sug.from_q,
+                    post_list=post_list
+                )
+            except Exception as e:
+                print(f"      ✗ 搜索失败: {e}")
+                return Search(
+                    text=sug.text,
+                    score_with_o=sug.score_with_o,
+                    from_q=sug.from_q,
+                    post_list=[]
+                )
+
+        search_tasks = [search_for_sug(sug) for sug in high_score_sugs]
+        search_list = await asyncio.gather(*search_tasks)
+
+    # 步骤4: 生成N域组合
+    print(f"\n[步骤4] 生成{round_num}域组合...")
+    domain_combinations = generate_domain_combinations(segments, round_num)
+    print(f"  生成了 {len(domain_combinations)} 个组合")
+
+    if len(domain_combinations) == 0:
+        print(f"  无法生成{round_num}域组合")
+        # 即使无法组合,也返回高分SUG作为下轮输入
+        q_list_next = []
+        for sug in all_sugs:
+            if sug.from_q and sug.score_with_o >= sug.from_q.score_with_o + REQUIRED_SCORE_GAIN:
+                q = Q(
+                    text=sug.text,
+                    score_with_o=sug.score_with_o,
+                    reason=sug.reason,
+                    from_source="sug",
+                    type_label=""
+                )
+                q_list_next.append(q)
+
+        round_data.update({
+            "domain_combinations_count": 0,
+            "sug_count": len(all_sugs),
+            "high_score_sug_count": len(high_score_sugs),
+            "search_count": len(search_list),
+            "sug_details": sug_details,
+            "q_list_next_size": len(q_list_next)
+        })
+        context.rounds.append(round_data)
+        return q_list_next, search_list
+
+    # 步骤5: 评估所有组合
+    print(f"\n[步骤5] 评估{len(domain_combinations)}个组合...")
+
+    async def evaluate_combination(comb: DomainCombination) -> DomainCombination:
+        async with semaphore:
+            # 🆕 根据轮次选择评估逻辑
+            if round_num == 1:
+                # Round 1: 域内评估(新逻辑)
+                comb.score_with_o, comb.reason = await evaluate_domain_combination_round1(
+                    comb, segments, context
+                )
+            else:
+                # Round 2+: 域间评估(新逻辑)
+                comb.score_with_o, comb.reason = await evaluate_domain_combination_round2plus(
+                    comb, segments, context
+                )
+
+            # 🆕 存储组合得分到历史记录
+            context.word_score_history[comb.text] = comb.score_with_o
+
+            return comb
+
+    eval_tasks = [evaluate_combination(comb) for comb in domain_combinations]
+    await asyncio.gather(*eval_tasks)
+
+    # 排序 - 已注释,保持原始顺序
+    # domain_combinations.sort(key=lambda x: x.score_with_o, reverse=True)
+
+    # 打印所有组合(保持原始顺序)
+    evaluation_strategy = 'Round 1 域内评估(品类×域得分)' if round_num == 1 else 'Round 2+ 域间评估(加权系数调整)'
+    print(f"  评估完成,共{len(domain_combinations)}个组合 [策略: {evaluation_strategy}]")
+    for i, comb in enumerate(domain_combinations, 1):
+        print(f"    {i}. {comb.text} {comb.type_label} (分数: {comb.score_with_o:.2f})")
+
+    # 为每个组合补充来源词分数信息,并判断是否超过所有来源词得分
+    for comb in domain_combinations:
+        word_details = []
+        flat_scores: list[float] = []
+        for domain_index, words in zip(comb.domains, comb.source_words):
+            segment = segments[domain_index] if 0 <= domain_index < len(segments) else None
+            segment_type = segment.type if segment else ""
+            segment_text = segment.text if segment else ""
+            items = []
+            for word in words:
+                score = 0.0
+                if segment and word in segment.word_scores:
+                    score = segment.word_scores[word]
+                items.append({
+                    "text": word,
+                    "score": score
+                })
+                flat_scores.append(score)
+            word_details.append({
+                "domain_index": domain_index,
+                "segment_type": segment_type,
+                "segment_text": segment_text,
+                "words": items
+            })
+        comb.source_word_details = word_details
+        comb.source_scores = flat_scores
+        comb.max_source_score = max(flat_scores) if flat_scores else None
+        comb.is_above_source_scores = bool(flat_scores) and all(
+            comb.score_with_o > score for score in flat_scores
+        )
+
+    # 步骤6: 构建 q_list_next(组合 + 高分SUG)
+    print(f"\n[步骤6] 生成下轮输入...")
+    q_list_next: list[Q] = []
+
+    # 6.1 添加高增益SUG(满足增益条件),并按分数排序
+    sug_candidates: list[tuple[Q, Sug]] = []
+    for sug in all_sugs:
+        if sug.from_q and sug.score_with_o >= sug.from_q.score_with_o + REQUIRED_SCORE_GAIN:
+            q = Q(
+                text=sug.text,
+                score_with_o=sug.score_with_o,
+                reason=sug.reason,
+                from_source="sug",
+                type_label=""
+            )
+            sug_candidates.append((q, sug))
+
+    sug_candidates.sort(key=lambda item: item[0].score_with_o, reverse=True)
+    q_list_next.extend([item[0] for item in sug_candidates])
+    high_gain_sugs = [item[1] for item in sug_candidates]
+    print(f"  添加 {len(high_gain_sugs)} 个高增益SUG(增益 ≥ {REQUIRED_SCORE_GAIN:.2f})")
+
+    # 6.2 添加高分组合(需超过所有来源词得分),并按分数排序
+    combination_candidates: list[tuple[Q, DomainCombination]] = []
+    for comb in domain_combinations:
+        if comb.is_above_source_scores and comb.score_with_o > 0:
+            domains_str = ','.join([f'D{d}' for d in comb.domains]) if comb.domains else ''
+            q = Q(
+                text=comb.text,
+                score_with_o=comb.score_with_o,
+                reason=comb.reason,
+                from_source="domain_comb",
+                type_label=comb.type_label,
+                domain_type=domains_str  # 添加域信息
+            )
+            combination_candidates.append((q, comb))
+
+    combination_candidates.sort(key=lambda item: item[0].score_with_o, reverse=True)
+    q_list_next.extend([item[0] for item in combination_candidates])
+    high_score_combinations = [item[1] for item in combination_candidates]
+    print(f"  添加 {len(high_score_combinations)} 个高分组合(组合得分 > 所有来源词)")
+
+    # 保存round数据(包含完整帖子信息)
+    search_results_data = []
+    for search in search_list:
+        search_results_data.append({
+            "text": search.text,
+            "score_with_o": search.score_with_o,
+            "post_list": [
+                {
+                    "note_id": post.note_id,
+                    "note_url": post.note_url,
+                    "title": post.title,
+                    "body_text": post.body_text,
+                    "images": post.images,
+                    "interact_info": post.interact_info
+                }
+                for post in search.post_list
+            ]
+        })
+
+    round_data.update({
+        "input_queries": [{"text": q.text, "score": q.score_with_o, "from_source": q.from_source, "type": "input", "domain_index": q.domain_index, "domain_type": q.domain_type} for q in query_input],
+        "domain_combinations_count": len(domain_combinations),
+        "domain_combinations": [
+            {
+                "text": comb.text,
+                "type_label": comb.type_label,
+                "score": comb.score_with_o,
+                "reason": comb.reason,
+                "domains": comb.domains,
+                "source_words": comb.source_words,
+                "from_segments": comb.from_segments,
+                "source_word_details": comb.source_word_details,
+                "source_scores": comb.source_scores,
+                "is_above_source_scores": comb.is_above_source_scores,
+                "max_source_score": comb.max_source_score
+            }
+            for comb in domain_combinations
+        ],
+        "high_score_combinations": [
+            {
+                "text": item[0].text,
+                "score": item[0].score_with_o,
+                "type_label": item[0].type_label,
+                "type": "combination",
+                "is_above_source_scores": item[1].is_above_source_scores
+            }
+            for item in combination_candidates
+        ],
+        "sug_count": len(all_sugs),
+        "sug_details": sug_details,
+        "high_score_sug_count": len(high_score_sugs),
+        "high_gain_sugs": [{"text": q.text, "score": q.score_with_o, "type": "sug"} for q in q_list_next if q.from_source == "sug"],
+        "search_count": len(search_list),
+        "search_results": search_results_data,
+        "q_list_next_size": len(q_list_next),
+        "q_list_next_sections": {
+            "sugs": [
+                {
+                    "text": item[0].text,
+                    "score": item[0].score_with_o,
+                    "from_source": "sug"
+                }
+                for item in sug_candidates
+            ],
+            "domain_combinations": [
+                {
+                    "text": item[0].text,
+                    "score": item[0].score_with_o,
+                    "from_source": "domain_comb",
+                    "is_above_source_scores": item[1].is_above_source_scores
+                }
+                for item in combination_candidates
+            ]
+        }
+    })
+    context.rounds.append(round_data)
+
+    print(f"\nRound {round_num} 总结:")
+    print(f"  输入Query数: {len(query_input)}")
+    print(f"  域组合数: {len(domain_combinations)}")
+    print(f"  高分组合: {len(high_score_combinations)}")
+    print(f"  SUG数: {len(all_sugs)}")
+    print(f"  高分SUG数: {len(high_score_sugs)}")
+    print(f"  高增益SUG: {len(high_gain_sugs)}")
+    print(f"  搜索数: {len(search_list)}")
+    print(f"  下轮Query数: {len(q_list_next)}")
+
+    return q_list_next, search_list
+
+
+async def iterative_loop_v2(
+    context: RunContext,
+    max_rounds: int = 4,
+    sug_threshold: float = 0.7
+):
+    """v121 主迭代循环"""
+
+    print(f"\n{'='*60}")
+    print(f"开始v121迭代循环(语义分段跨域组词版)")
+    print(f"最大轮数: {max_rounds}")
+    print(f"sug阈值: {sug_threshold}")
+    print(f"{'='*60}")
+
+    # Round 0: 初始化(语义分段 + 拆词)
+    segments = await initialize_v2(context.o, context)
+
+    # API实例
+    xiaohongshu_api = XiaohongshuSearchRecommendations()
+    xiaohongshu_search = XiaohongshuSearch()
+
+    # 收集所有搜索结果
+    all_search_list = []
+
+    # 准备 Round 1 的输入:从 segments 提取所有 words
+    query_input = extract_words_from_segments(segments)
+    print(f"\n提取了 {len(query_input)} 个词作为 Round 1 的输入")
+
+    # Round 1-N: 迭代循环
+    num_segments = len(segments)
+    actual_max_rounds = min(max_rounds, num_segments)
+    round_num = 1
+
+    while query_input and round_num <= actual_max_rounds:
+        query_input, search_list = await run_round_v2(
+            round_num=round_num,
+            query_input=query_input,  # 传递上一轮的输出
+            segments=segments,
+            o=context.o,
+            context=context,
+            xiaohongshu_api=xiaohongshu_api,
+            xiaohongshu_search=xiaohongshu_search,
+            sug_threshold=sug_threshold
+        )
+
+        all_search_list.extend(search_list)
+
+        # 如果没有新的query,提前结束
+        if not query_input:
+            print(f"\n第{round_num}轮后无新query生成,提前结束迭代")
+            break
+
+        round_num += 1
+
+    print(f"\n{'='*60}")
+    print(f"迭代完成")
+    print(f"  实际轮数: {round_num}")
+    print(f"  总搜索次数: {len(all_search_list)}")
+    print(f"  总帖子数: {sum(len(s.post_list) for s in all_search_list)}")
+    print(f"{'='*60}")
+
+    return all_search_list
+
+
+# ============================================================================
+# 主函数
+# ============================================================================
+
+async def main(input_dir: str, max_rounds: int = 2, sug_threshold: float = 0.7, visualize: bool = False):
+    """主函数"""
+    current_time, log_url = set_trace()
+
+    # 读取输入
+    input_context_file = os.path.join(input_dir, 'context.md')
+    input_q_file = os.path.join(input_dir, 'q.md')
+
+    c = read_file_as_string(input_context_file)  # 原始需求
+    o = read_file_as_string(input_q_file)  # 原始问题
+
+    # 版本信息
+    version = os.path.basename(__file__)
+    version_name = os.path.splitext(version)[0]
+
+    # 日志目录
+    log_dir = os.path.join(input_dir, "output", version_name, current_time)
+
+    # 创建运行上下文
+    run_context = RunContext(
+        version=version,
+        input_files={
+            "input_dir": input_dir,
+            "context_file": input_context_file,
+            "q_file": input_q_file,
+        },
+        c=c,
+        o=o,
+        log_dir=log_dir,
+        log_url=log_url,
+    )
+
+    # 创建日志目录
+    os.makedirs(run_context.log_dir, exist_ok=True)
+
+    # 配置日志文件
+    log_file_path = os.path.join(run_context.log_dir, "run.log")
+    log_file = open(log_file_path, 'w', encoding='utf-8')
+
+    # 重定向stdout到TeeLogger(同时输出到控制台和文件)
+    original_stdout = sys.stdout
+    sys.stdout = TeeLogger(original_stdout, log_file)
+
+    try:
+        print(f"📝 日志文件: {log_file_path}")
+        print(f"{'='*60}\n")
+
+        # 执行迭代 (v121: 使用新架构)
+        all_search_list = await iterative_loop_v2(
+            run_context,
+            max_rounds=max_rounds,
+            sug_threshold=sug_threshold
+        )
+
+        # 格式化输出
+        output = f"原始需求:{run_context.c}\n"
+        output += f"原始问题:{run_context.o}\n"
+        output += f"总搜索次数:{len(all_search_list)}\n"
+        output += f"总帖子数:{sum(len(s.post_list) for s in all_search_list)}\n"
+        output += "\n" + "="*60 + "\n"
+
+        if all_search_list:
+            output += "【搜索结果】\n\n"
+            for idx, search in enumerate(all_search_list, 1):
+                output += f"{idx}. 搜索词: {search.text} (分数: {search.score_with_o:.2f})\n"
+                output += f"   帖子数: {len(search.post_list)}\n"
+                if search.post_list:
+                    for post_idx, post in enumerate(search.post_list[:3], 1):  # 只显示前3个
+                        output += f"   {post_idx}) {post.title}\n"
+                        output += f"      URL: {post.note_url}\n"
+                output += "\n"
+        else:
+            output += "未找到搜索结果\n"
+
+        run_context.final_output = output
+
+        print(f"\n{'='*60}")
+        print("最终结果")
+        print(f"{'='*60}")
+        print(output)
+
+        # 保存上下文文件
+        context_file_path = os.path.join(run_context.log_dir, "run_context.json")
+        context_dict = run_context.model_dump()
+        with open(context_file_path, "w", encoding="utf-8") as f:
+            json.dump(context_dict, f, ensure_ascii=False, indent=2)
+        print(f"\nRunContext saved to: {context_file_path}")
+
+        # 保存详细的搜索结果
+        search_results_path = os.path.join(run_context.log_dir, "search_results.json")
+        search_results_data = [s.model_dump() for s in all_search_list]
+        with open(search_results_path, "w", encoding="utf-8") as f:
+            json.dump(search_results_data, f, ensure_ascii=False, indent=2)
+        print(f"Search results saved to: {search_results_path}")
+
+        # 可视化
+        if visualize:
+            import subprocess
+            output_html = os.path.join(run_context.log_dir, "visualization.html")
+            print(f"\n🎨 生成可视化HTML...")
+
+            # 获取绝对路径
+            abs_context_file = os.path.abspath(context_file_path)
+            abs_output_html = os.path.abspath(output_html)
+
+            # 运行可视化脚本
+            result = subprocess.run([
+                "node",
+                "visualization/sug_v6_1_2_121/index.js",
+                abs_context_file,
+                abs_output_html
+            ])
+
+            if result.returncode == 0:
+                print(f"✅ 可视化已生成: {output_html}")
+            else:
+                print(f"❌ 可视化生成失败")
+
+    finally:
+        # 恢复stdout
+        sys.stdout = original_stdout
+        log_file.close()
+        print(f"\n📝 运行日志已保存: {log_file_path}")
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description="搜索query优化工具 - v6.1.2.121 语义分段跨域组词版")
+    parser.add_argument(
+        "--input-dir",
+        type=str,
+        default="input/旅游-逸趣玩旅行/如何获取能体现川西秋季特色的高质量风光摄影素材?",
+        help="输入目录路径,默认: input/旅游-逸趣玩旅行/如何获取能体现川西秋季特色的高质量风光摄影素材?"
+    )
+    parser.add_argument(
+        "--max-rounds",
+        type=int,
+        default=4,
+        help="最大轮数,默认: 4"
+    )
+    parser.add_argument(
+        "--sug-threshold",
+        type=float,
+        default=0.7,
+        help="suggestion阈值,默认: 0.7"
+    )
+    parser.add_argument(
+        "--visualize",
+        action="store_true",
+        default=True,
+        help="运行完成后自动生成可视化HTML"
+    )
+    args = parser.parse_args()
+
+    asyncio.run(main(args.input_dir, max_rounds=args.max_rounds, sug_threshold=args.sug_threshold, visualize=args.visualize))

+ 0 - 811
sug_v6_1_2_2.py

@@ -1,811 +0,0 @@
-import asyncio
-import json
-import os
-import argparse
-from datetime import datetime
-
-from agents import Agent, Runner
-from lib.my_trace import set_trace
-from typing import Literal
-from pydantic import BaseModel, Field
-
-from lib.utils import read_file_as_string
-from lib.client import get_model
-MODEL_NAME = "google/gemini-2.5-flash"
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-
-    # 步骤化日志
-    steps: list[dict] = Field(default_factory=list, description="执行步骤的详细记录")
-
-    # 探索阶段记录(保留用于向后兼容)
-    keywords: list[str] | None = Field(default=None, description="提取的关键词")
-    exploration_levels: list[dict] = Field(default_factory=list, description="每一层的探索结果")
-    level_analyses: list[dict] = Field(default_factory=list, description="每一层的主Agent分析")
-
-    # 最终结果
-    final_candidates: list[str] | None = Field(default=None, description="最终选出的候选query")
-    evaluation_results: list[dict] | None = Field(default=None, description="候选query的评估结果")
-    optimization_result: dict | None = Field(default=None, description="最终优化结果对象")
-    final_output: str | None = Field(default=None, description="最终输出结果(格式化文本)")
-
-
-# ============================================================================
-# Agent 1: 关键词提取专家
-# ============================================================================
-keyword_extraction_instructions = """
-你是关键词提取专家。给定一个搜索问题(含上下文),提取出**最细粒度的关键概念**。
-
-## 提取原则
-
-1. **细粒度优先**:拆分成最小的有意义单元
-   - 不要保留完整的长句
-   - 拆分成独立的、有搜索意义的词或短语
-
-2. **保留核心维度**:
-   - 地域/对象
-   - 时间
-   - 行为/意图:获取、教程、推荐、如何等
-   - 主题/领域
-   - 质量/属性
-
-3. **去掉无意义的虚词**:的、吗、呢等
-
-4. **保留领域专有词**:不要过度拆分专业术语
-   - 如果是常见的组合词,保持完整
-
-## 输出要求
-
-输出关键词列表,按重要性排序(最核心的在前)。
-""".strip()
-
-class KeywordList(BaseModel):
-    """关键词列表"""
-    keywords: list[str] = Field(..., description="提取的关键词,按重要性排序")
-    reasoning: str = Field(..., description="提取理由")
-
-keyword_extractor = Agent[None](
-    name="关键词提取专家",
-    instructions=keyword_extraction_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=KeywordList,
-)
-
-
-# ============================================================================
-# Agent 2: 层级探索分析专家
-# ============================================================================
-level_analysis_instructions = """
-你是搜索空间探索分析专家。基于当前层级的探索结果,决定下一步行动。
-
-## 你的任务
-
-分析当前已探索的词汇空间,判断:
-1. **发现了什么有价值的信号?**
-2. **是否已经可以评估候选了?**
-3. **如果还不够,下一层应该探索什么组合?**
-
-## 分析维度
-
-### 1. 信号识别(最重要)
-
-看推荐词里**出现了什么主题**:
-
-**关键问题:**
-- 哪些推荐词**最接近原始需求**?
-- 哪些推荐词**揭示了有价值的方向**(即使不完全匹配)?
-- 哪些推荐词可以作为**下一层探索的桥梁**?
-- 系统对哪些概念理解得好?哪些理解偏了?
-
-### 2. 组合策略
-
-基于发现的信号,设计下一层探索:
-
-**组合类型:**
-
-a) **关键词直接组合**
-   - 两个关键词组合成新query
-
-b) **利用推荐词作为桥梁**(重要!)
-   - 发现某个推荐词很有价值 → 直接探索这个推荐词
-   - 或在推荐词基础上加其他关键词
-
-c) **跨层级组合**
-   - 结合多层发现的有价值推荐词
-   - 组合成更复杂的query
-
-### 3. 停止条件
-
-**何时可以评估候选?**
-
-满足以下之一:
-- 推荐词中出现了**明确包含原始需求多个核心要素的query**
-- 已经探索到**足够复杂的组合**(3-4个关键词),且推荐词相关
-- 探索了**3-4层**,信息已经足够丰富
-
-**何时继续探索?**
-- 当前推荐词太泛,没有接近原始需求
-- 发现了有价值的信号,但需要进一步组合验证
-- 层数还少(< 3层)
-
-## 输出要求
-
-### 1. key_findings
-总结当前层发现的关键信息,包括:
-- 哪些推荐词最有价值?
-- 系统对哪些概念理解得好/不好?
-- 发现了什么意外的方向?
-
-### 2. promising_signals
-列出最有价值的推荐词(来自任何已探索的query),每个说明为什么有价值
-
-### 3. should_evaluate_now
-是否已经可以开始评估候选了?true/false
-
-### 4. candidates_to_evaluate
-如果should_evaluate_now=true,列出应该评估的候选query
-- 可以是推荐词
-- 可以是自己构造的组合
-
-### 5. next_combinations
-如果should_evaluate_now=false,列出下一层应该探索的query组合
-
-### 6. reasoning
-详细的推理过程
-
-## 重要原则
-
-1. **不要过早评估**:至少探索2层,除非第一层就发现了完美匹配
-2. **充分利用推荐词**:推荐词是系统给的提示,要善用
-3. **保持探索方向的多样性**:不要只盯着一个方向
-4. **识别死胡同**:如果某个方向的推荐词一直不相关,果断放弃
-""".strip()
-
-class PromisingSignal(BaseModel):
-    """有价值的推荐词信号"""
-    query: str = Field(..., description="推荐词")
-    from_level: int = Field(..., description="来自哪一层")
-    reason: str = Field(..., description="为什么有价值")
-
-class LevelAnalysis(BaseModel):
-    """层级分析结果"""
-    key_findings: str = Field(..., description="当前层的关键发现")
-    promising_signals: list[PromisingSignal] = Field(..., description="有价值的推荐词信号")
-    should_evaluate_now: bool = Field(..., description="是否应该开始评估候选")
-    candidates_to_evaluate: list[str] = Field(default_factory=list, description="如果should_evaluate_now=true,要评估的候选query列表")
-    next_combinations: list[str] = Field(default_factory=list, description="如果should_evaluate_now=false,下一层要探索的query组合")
-    reasoning: str = Field(..., description="详细的推理过程")
-
-level_analyzer = Agent[None](
-    name="层级探索分析专家",
-    instructions=level_analysis_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=LevelAnalysis,
-)
-
-
-# ============================================================================
-# Agent 3: 评估专家(简化版:意图匹配 + 相关性评分)
-# ============================================================================
-eval_instructions = """
-你是搜索query评估专家。给定原始问题和推荐query,评估两个维度。
-
-## 评估目标
-
-用这个推荐query搜索,能否找到满足原始需求的内容?
-
-## 两层评分
-
-### 1. intent_match(意图匹配)= true/false
-
-推荐query的**使用意图**是否与原问题一致?
-
-**核心问题:用户搜索这个推荐词,想做什么?**
-
-**判断标准:**
-- 原问题意图:找方法?找教程?找资源/素材?找工具?看作品?
-- 推荐词意图:如果用户搜索这个词,他的目的是什么?
-
-**示例:**
-- 原问题意图="找素材"
-  - ✅ true: "素材下载"、"素材网站"、"免费素材"(都是获取素材)
-  - ❌ false: "素材制作教程"、"如何制作素材"(意图变成学习了)
-
-- 原问题意图="学教程"
-  - ✅ true: "教程视频"、"教学步骤"、"入门指南"
-  - ❌ false: "成品展示"、"作品欣赏"(意图变成看作品了)
-
-**评分:**
-- true = 意图一致,搜索推荐词能达到原问题的目的
-- false = 意图改变,搜索推荐词无法达到原问题的目的
-
-### 2. relevance_score(相关性)= 0-1 连续分数
-
-推荐query在**主题、要素、属性**上与原问题的相关程度?
-
-**评估维度:**
-- 主题相关:核心主题是否匹配?(如:摄影、旅游、美食)
-- 要素覆盖:关键要素保留了多少?(如:地域、时间、对象、工具)
-- 属性匹配:质量、风格、特色等属性是否保留?
-
-**评分参考:**
-- 0.9-1.0 = 几乎完美匹配,所有核心要素都保留
-- 0.7-0.8 = 高度相关,核心要素保留,少数次要要素缺失
-- 0.5-0.6 = 中度相关,主题匹配但多个要素缺失
-- 0.3-0.4 = 低度相关,只有部分主题相关
-- 0-0.2 = 基本不相关
-
-## 评估策略
-
-1. **先判断 intent_match**:意图不匹配直接 false,无论相关性多高
-2. **再评估 relevance_score**:在意图匹配的前提下,计算相关性
-
-## 输出要求
-
-- intent_match: true/false
-- relevance_score: 0-1 的浮点数
-- reason: 详细的评估理由,需要说明:
-  - 原问题的意图是什么
-  - 推荐词的意图是什么
-  - 为什么判断意图匹配/不匹配
-  - 相关性分数的依据(哪些要素保留/缺失)
-""".strip()
-
-class RelevanceEvaluation(BaseModel):
-    """评估反馈模型 - 意图匹配 + 相关性"""
-    intent_match: bool = Field(..., description="意图是否匹配")
-    relevance_score: float = Field(..., description="相关性分数 0-1,分数越高越相关")
-    reason: str = Field(..., description="评估理由,需说明意图判断和相关性依据")
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=RelevanceEvaluation,
-)
-
-
-# ============================================================================
-# 日志辅助函数
-# ============================================================================
-
-def add_step(context: RunContext, step_name: str, step_type: str, data: dict):
-    """添加步骤记录"""
-    step = {
-        "step_number": len(context.steps) + 1,
-        "step_name": step_name,
-        "step_type": step_type,
-        "timestamp": datetime.now().isoformat(),
-        "data": data
-    }
-    context.steps.append(step)
-    return step
-
-
-# ============================================================================
-# 核心函数
-# ============================================================================
-
-async def extract_keywords(q: str, context: RunContext) -> KeywordList:
-    """提取关键词"""
-    print("\n[步骤 1] 正在提取关键词...")
-    result = await Runner.run(keyword_extractor, q)
-    keyword_list: KeywordList = result.final_output
-    print(f"提取的关键词:{keyword_list.keywords}")
-    print(f"提取理由:{keyword_list.reasoning}")
-
-    # 记录步骤
-    add_step(context, "提取关键词", "keyword_extraction", {
-        "input_question": q,
-        "keywords": keyword_list.keywords,
-        "reasoning": keyword_list.reasoning
-    })
-
-    return keyword_list
-
-
-async def explore_level(queries: list[str], level_num: int, context: RunContext) -> dict:
-    """探索一个层级(并发获取所有query的推荐词)"""
-    step_num = len(context.steps) + 1
-    print(f"\n{'='*60}")
-    print(f"[步骤 {step_num}] Level {level_num} 探索:{len(queries)} 个query")
-    print(f"{'='*60}")
-
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-
-    # 并发获取所有推荐词
-    async def get_single_sug(query: str):
-        print(f"  探索: {query}")
-        suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-        print(f"    → {len(suggestions) if suggestions else 0} 个推荐词")
-        return {
-            "query": query,
-            "suggestions": suggestions or []
-        }
-
-    results = await asyncio.gather(*[get_single_sug(q) for q in queries])
-
-    level_data = {
-        "level": level_num,
-        "timestamp": datetime.now().isoformat(),
-        "queries": results
-    }
-
-    context.exploration_levels.append(level_data)
-
-    # 记录步骤
-    add_step(context, f"Level {level_num} 探索", "level_exploration", {
-        "level": level_num,
-        "input_queries": queries,
-        "query_count": len(queries),
-        "results": results,
-        "total_suggestions": sum(len(r['suggestions']) for r in results)
-    })
-
-    return level_data
-
-
-async def analyze_level(level_data: dict, all_levels: list[dict], original_question: str, context: RunContext) -> LevelAnalysis:
-    """分析当前层级,决定下一步"""
-    step_num = len(context.steps) + 1
-    print(f"\n[步骤 {step_num}] 正在分析 Level {level_data['level']}...")
-
-    # 构造输入
-    analysis_input = f"""
-<原始问题>
-{original_question}
-</原始问题>
-
-<已探索的所有层级>
-{json.dumps(all_levels, ensure_ascii=False, indent=2)}
-</已探索的所有层级>
-
-<当前层级>
-Level {level_data['level']}
-{json.dumps(level_data['queries'], ensure_ascii=False, indent=2)}
-</当前层级>
-
-请分析当前探索状态,决定下一步行动。
-"""
-
-    result = await Runner.run(level_analyzer, analysis_input)
-    analysis: LevelAnalysis = result.final_output
-
-    print(f"\n分析结果:")
-    print(f"  关键发现:{analysis.key_findings}")
-    print(f"  有价值的信号:{len(analysis.promising_signals)} 个")
-    print(f"  是否评估:{analysis.should_evaluate_now}")
-
-    if analysis.should_evaluate_now:
-        print(f"  候选query:{analysis.candidates_to_evaluate}")
-    else:
-        print(f"  下一层探索:{analysis.next_combinations}")
-
-    # 保存分析结果
-    context.level_analyses.append({
-        "level": level_data['level'],
-        "timestamp": datetime.now().isoformat(),
-        "analysis": analysis.model_dump()
-    })
-
-    # 记录步骤
-    add_step(context, f"Level {level_data['level']} 分析", "level_analysis", {
-        "level": level_data['level'],
-        "key_findings": analysis.key_findings,
-        "promising_signals_count": len(analysis.promising_signals),
-        "promising_signals": [s.model_dump() for s in analysis.promising_signals],
-        "should_evaluate_now": analysis.should_evaluate_now,
-        "candidates_to_evaluate": analysis.candidates_to_evaluate if analysis.should_evaluate_now else [],
-        "next_combinations": analysis.next_combinations if not analysis.should_evaluate_now else [],
-        "reasoning": analysis.reasoning
-    })
-
-    return analysis
-
-
-async def evaluate_candidates(candidates: list[str], original_question: str, context: RunContext) -> list[dict]:
-    """评估候选query"""
-    step_num = len(context.steps) + 1
-    print(f"\n{'='*60}")
-    print(f"[步骤 {step_num}] 评估 {len(candidates)} 个候选query")
-    print(f"{'='*60}")
-
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-
-    async def evaluate_single_candidate(candidate: str):
-        print(f"\n评估候选:{candidate}")
-
-        # 1. 获取推荐词
-        suggestions = xiaohongshu_api.get_recommendations(keyword=candidate)
-        print(f"  获取到 {len(suggestions) if suggestions else 0} 个推荐词")
-
-        if not suggestions:
-            return {
-                "candidate": candidate,
-                "suggestions": [],
-                "evaluations": []
-            }
-
-        # 2. 评估每个推荐词
-        async def eval_single_sug(sug: str):
-            eval_input = f"""
-<原始问题>
-{original_question}
-</原始问题>
-
-<待评估的推荐query>
-{sug}
-</待评估的推荐query>
-
-请评估该推荐query:
-1. intent_match: 意图是否匹配(true/false)
-2. relevance_score: 相关性分数(0-1)
-3. reason: 详细的评估理由
-"""
-            result = await Runner.run(evaluator, eval_input)
-            evaluation: RelevanceEvaluation = result.final_output
-            return {
-                "query": sug,
-                "intent_match": evaluation.intent_match,
-                "relevance_score": evaluation.relevance_score,
-                "reason": evaluation.reason,
-            }
-
-        evaluations = await asyncio.gather(*[eval_single_sug(s) for s in suggestions])
-
-        return {
-            "candidate": candidate,
-            "suggestions": suggestions,
-            "evaluations": evaluations
-        }
-
-    results = await asyncio.gather(*[evaluate_single_candidate(c) for c in candidates])
-
-    context.evaluation_results = results
-
-    # 记录步骤
-    add_step(context, "评估候选query", "candidate_evaluation", {
-        "candidate_count": len(candidates),
-        "candidates": candidates,
-        "results": results,
-        "total_evaluations": sum(len(r['evaluations']) for r in results)
-    })
-
-    return results
-
-
-def find_qualified_queries(evaluation_results: list[dict], min_relevance_score: float = 0.7) -> list[dict]:
-    """
-    查找所有合格的query
-
-    筛选标准:
-    1. intent_match = True(必须满足)
-    2. relevance_score >= min_relevance_score
-
-    返回:按 relevance_score 降序排列
-    """
-    all_qualified = []
-
-    for result in evaluation_results:
-        for eval_item in result.get("evaluations", []):
-            if (eval_item['intent_match'] is True
-                and eval_item['relevance_score'] >= min_relevance_score):
-                all_qualified.append({
-                    "from_candidate": result["candidate"],
-                    **eval_item
-                })
-
-    # 按relevance_score降序排列
-    return sorted(all_qualified, key=lambda x: x['relevance_score'], reverse=True)
-
-
-# ============================================================================
-# 主流程
-# ============================================================================
-
-async def progressive_exploration(context: RunContext, max_levels: int = 4) -> dict:
-    """
-    渐进式广度探索流程
-
-    Args:
-        context: 运行上下文
-        max_levels: 最大探索层数,默认4
-
-    返回格式:
-    {
-        "success": True/False,
-        "results": [...],
-        "message": "..."
-    }
-    """
-
-    # 阶段1:提取关键词(从原始问题提取)
-    keyword_result = await extract_keywords(context.q, context)
-    context.keywords = keyword_result.keywords
-
-    # 阶段2:渐进式探索
-    current_level = 1
-
-    # Level 1:单个关键词
-    level_1_queries = context.keywords[:7]  # 限制最多7个关键词
-    level_1_data = await explore_level(level_1_queries, current_level, context)
-
-    # 分析Level 1
-    analysis_1 = await analyze_level(level_1_data, context.exploration_levels, context.q, context)
-
-    if analysis_1.should_evaluate_now:
-        # 直接评估
-        eval_results = await evaluate_candidates(analysis_1.candidates_to_evaluate, context.q, context)
-        qualified = find_qualified_queries(eval_results, min_relevance_score=0.7)
-
-        if qualified:
-            return {
-                "success": True,
-                "results": qualified,
-                "message": f"Level 1 即找到 {len(qualified)} 个合格query"
-            }
-
-    # Level 2 及以后:迭代探索
-    for level_num in range(2, max_levels + 1):
-        # 获取上一层的分析结果
-        prev_analysis: LevelAnalysis = context.level_analyses[-1]["analysis"]
-        prev_analysis = LevelAnalysis(**prev_analysis)  # 转回对象
-
-        if not prev_analysis.next_combinations:
-            print(f"\nLevel {level_num-1} 分析后无需继续探索")
-            break
-
-        # 探索当前层
-        level_data = await explore_level(prev_analysis.next_combinations, level_num, context)
-
-        # 分析当前层
-        analysis = await analyze_level(level_data, context.exploration_levels, context.q, context)
-
-        if analysis.should_evaluate_now:
-            # 评估候选
-            eval_results = await evaluate_candidates(analysis.candidates_to_evaluate, context.q, context)
-            qualified = find_qualified_queries(eval_results, min_relevance_score=0.7)
-
-            if qualified:
-                return {
-                    "success": True,
-                    "results": qualified,
-                    "message": f"Level {level_num} 找到 {len(qualified)} 个合格query"
-                }
-
-    # 所有层探索完,降低标准
-    print(f"\n{'='*60}")
-    print(f"探索完 {max_levels} 层,降低标准(relevance_score >= 0.5)")
-    print(f"{'='*60}")
-
-    if context.evaluation_results:
-        acceptable = find_qualified_queries(context.evaluation_results, min_relevance_score=0.5)
-        if acceptable:
-            return {
-                "success": True,
-                "results": acceptable,
-                "message": f"找到 {len(acceptable)} 个可接受query(soft_score >= 0.5)"
-            }
-
-    # 完全失败
-    return {
-        "success": False,
-        "results": [],
-        "message": "探索完所有层级,未找到合格的推荐词"
-    }
-
-
-# ============================================================================
-# 输出格式化
-# ============================================================================
-
-def format_output(optimization_result: dict, context: RunContext) -> str:
-    """格式化输出结果"""
-    results = optimization_result.get("results", [])
-
-    output = f"原始问题:{context.q}\n"
-    output += f"提取的关键词:{', '.join(context.keywords or [])}\n"
-    output += f"探索层数:{len(context.exploration_levels)}\n"
-    output += f"状态:{optimization_result['message']}\n\n"
-
-    if optimization_result["success"] and results:
-        output += "合格的推荐query(按relevance_score降序):\n"
-        for i, result in enumerate(results, 1):
-            output += f"\n{i}. {result['query']}\n"
-            output += f"   - 来自候选:{result['from_candidate']}\n"
-            output += f"   - 意图匹配:{result['intent_match']} (True=意图一致)\n"
-            output += f"   - 相关性分数:{result['relevance_score']:.2f} (0-1,越高越相关)\n"
-            output += f"   - 评估理由:{result['reason']}\n"
-    else:
-        output += "结果:未找到合格推荐query\n"
-        if context.level_analyses:
-            last_analysis = context.level_analyses[-1]["analysis"]
-            output += f"\n最后一层分析:\n{last_analysis.get('key_findings', 'N/A')}\n"
-
-    return output.strip()
-
-
-# ============================================================================
-# 主函数
-# ============================================================================
-
-async def main(input_dir: str, max_levels: int = 4):
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]
-
-    # 日志保存目录
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-
-    # 执行渐进式探索
-    optimization_result = await progressive_exploration(run_context, max_levels=max_levels)
-
-    # 格式化输出
-    final_output = format_output(optimization_result, run_context)
-    print(f"\n{'='*60}")
-    print("最终结果")
-    print(f"{'='*60}")
-    print(final_output)
-
-    # 保存结果
-    run_context.optimization_result = optimization_result
-    run_context.final_output = final_output
-
-    # 记录最终输出步骤(保存完整的结果详情)
-    qualified_results = optimization_result.get("results", [])
-    add_step(run_context, "生成最终结果", "final_result", {
-        "success": optimization_result["success"],
-        "message": optimization_result["message"],
-        "qualified_query_count": len(qualified_results),
-        "qualified_queries": [r["query"] for r in qualified_results],  # 保存所有合格query
-        "qualified_results_detail": [  # 保存完整的评估详情
-            {
-                "rank": idx + 1,
-                "query": r["query"],
-                "from_candidate": r["from_candidate"],
-                "intent_match": r["intent_match"],
-                "relevance_score": r["relevance_score"],
-                "reason": r["reason"]
-            }
-            for idx, r in enumerate(qualified_results)
-        ],
-        "final_output": final_output
-    })
-
-    # 保存 RunContext 到 log_dir
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-    # 保存步骤化日志(更直观的格式)
-    steps_file_path = os.path.join(run_context.log_dir, "steps.json")
-    with open(steps_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.steps, f, ensure_ascii=False, indent=2)
-    print(f"Steps log saved to: {steps_file_path}")
-
-    # 生成步骤化的可读文本日志
-    steps_text_path = os.path.join(run_context.log_dir, "steps.md")
-    with open(steps_text_path, "w", encoding="utf-8") as f:
-        f.write(f"# 执行步骤日志\n\n")
-        f.write(f"**原始问题**: {run_context.q}\n\n")
-        f.write(f"**执行版本**: {run_context.version}\n\n")
-        f.write(f"**总步骤数**: {len(run_context.steps)}\n\n")
-        f.write("---\n\n")
-
-        for step in run_context.steps:
-            f.write(f"## 步骤 {step['step_number']}: {step['step_name']}\n\n")
-            f.write(f"**类型**: `{step['step_type']}`\n\n")
-            f.write(f"**时间**: {step['timestamp']}\n\n")
-
-            # 根据不同类型格式化数据
-            if step['step_type'] == 'keyword_extraction':
-                f.write(f"**提取的关键词**: {', '.join(step['data']['keywords'])}\n\n")
-                f.write(f"**提取理由**: {step['data']['reasoning']}\n\n")
-
-            elif step['step_type'] == 'level_exploration':
-                f.write(f"**探索层级**: Level {step['data']['level']}\n\n")
-                f.write(f"**输入query数量**: {step['data']['query_count']}\n\n")
-                f.write(f"**总推荐词数**: {step['data']['total_suggestions']}\n\n")
-                f.write(f"**探索的query**: {', '.join(step['data']['input_queries'])}\n\n")
-
-            elif step['step_type'] == 'level_analysis':
-                f.write(f"**关键发现**: {step['data']['key_findings']}\n\n")
-                f.write(f"**有价值信号数**: {step['data']['promising_signals_count']}\n\n")
-                f.write(f"**是否评估**: {step['data']['should_evaluate_now']}\n\n")
-                if step['data']['should_evaluate_now']:
-                    f.write(f"**候选query**: {', '.join(step['data']['candidates_to_evaluate'])}\n\n")
-                else:
-                    f.write(f"**下一层探索**: {', '.join(step['data']['next_combinations'])}\n\n")
-
-            elif step['step_type'] == 'candidate_evaluation':
-                f.write(f"**评估候选数**: {step['data']['candidate_count']}\n\n")
-                f.write(f"**候选query**: {', '.join(step['data']['candidates'])}\n\n")
-                f.write(f"**总评估数**: {step['data']['total_evaluations']}\n\n")
-
-            elif step['step_type'] == 'final_result':
-                f.write(f"**执行状态**: {'✅ 成功' if step['data']['success'] else '❌ 失败'}\n\n")
-                f.write(f"**结果消息**: {step['data']['message']}\n\n")
-                f.write(f"**合格query数量**: {step['data']['qualified_query_count']}\n\n")
-
-                # 显示详细的评估结果
-                if step['data'].get('qualified_results_detail'):
-                    f.write(f"### 合格的query详情\n\n")
-                    for result in step['data']['qualified_results_detail']:
-                        f.write(f"#### {result['rank']}. {result['query']}\n\n")
-                        f.write(f"- **来自候选**: {result['from_candidate']}\n")
-                        f.write(f"- **意图匹配**: {'✅ 是' if result['intent_match'] else '❌ 否'}\n")
-                        f.write(f"- **相关性分数**: {result['relevance_score']:.2f}\n")
-                        f.write(f"- **评估理由**: {result['reason']}\n\n")
-                elif step['data']['qualified_queries']:
-                    # 兼容旧格式(如果没有详情)
-                    f.write(f"**合格的query列表**:\n")
-                    for idx, q in enumerate(step['data']['qualified_queries'], 1):
-                        f.write(f"  {idx}. {q}\n")
-                    f.write("\n")
-
-                f.write(f"### 完整输出\n\n```\n{step['data']['final_output']}\n```\n\n")
-
-            f.write("---\n\n")
-
-    print(f"Steps markdown saved to: {steps_text_path}")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具 - v6.1 意图匹配+相关性评分版")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    parser.add_argument(
-        "--max-levels",
-        type=int,
-        default=4,
-        help="最大探索层数,默认: 4"
-    )
-    args = parser.parse_args()
-
-    asyncio.run(main(args.input_dir, max_levels=args.max_levels))

+ 0 - 1908
sug_v6_1_2_3.py

@@ -1,1908 +0,0 @@
-import asyncio
-import json
-import os
-import sys
-import argparse
-from datetime import datetime
-
-from agents import Agent, Runner
-from lib.my_trace import set_trace
-from typing import Literal
-from pydantic import BaseModel, Field
-
-from lib.utils import read_file_as_string
-from lib.client import get_model
-MODEL_NAME = "google/gemini-2.5-flash"
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-from script.search.xiaohongshu_search import XiaohongshuSearch
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-
-    # 步骤化日志
-    steps: list[dict] = Field(default_factory=list, description="执行步骤的详细记录")
-
-    # 探索阶段记录(保留用于向后兼容)
-    keywords: list[str] | None = Field(default=None, description="提取的关键词")
-    exploration_levels: list[dict] = Field(default_factory=list, description="每一层的探索结果")
-    level_analyses: list[dict] = Field(default_factory=list, description="每一层的主Agent分析")
-
-    # 最终结果
-    final_candidates: list[str] | None = Field(default=None, description="最终选出的候选query")
-    evaluation_results: list[dict] | None = Field(default=None, description="候选query的评估结果")
-    optimization_result: dict | None = Field(default=None, description="最终优化结果对象")
-    final_output: str | None = Field(default=None, description="最终输出结果(格式化文本)")
-
-
-# ============================================================================
-# Agent 1: 关键词提取专家
-# ============================================================================
-keyword_extraction_instructions = """
-你是关键词提取专家。给定一个搜索问题(含上下文),提取出**最细粒度的关键概念**。
-
-## 提取原则
-
-1. **细粒度优先**:拆分成最小的有意义单元
-   - 不要保留完整的长句
-   - 拆分成独立的、有搜索意义的词或短语
-
-2. **保留核心维度**:
-   - 地域/对象
-   - 时间
-   - 行为/意图:获取、教程、推荐、如何等
-   - 主题/领域
-   - 质量/属性
-
-3. **去掉无意义的虚词**:的、吗、呢等
-
-4. **保留领域专有词**:不要过度拆分专业术语
-   - 如果是常见的组合词,保持完整
-
-## 输出要求
-
-输出关键词列表,按重要性排序(最核心的在前)。
-""".strip()
-
-class KeywordList(BaseModel):
-    """关键词列表"""
-    keywords: list[str] = Field(..., description="提取的关键词,按重要性排序")
-    reasoning: str = Field(..., description="提取理由")
-
-keyword_extractor = Agent[None](
-    name="关键词提取专家",
-    instructions=keyword_extraction_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=KeywordList,
-)
-
-
-# ============================================================================
-# Agent 2: 层级探索分析专家
-# ============================================================================
-level_analysis_instructions = """
-你是搜索空间探索分析专家。基于当前层级的探索结果,决定下一步行动。
-
-## 你的任务
-
-分析当前已探索的词汇空间,判断:
-1. **发现了什么有价值的信号?**
-2. **是否已经可以评估候选了?**
-3. **如果还不够,下一层应该探索什么组合?**
-
-## 分析维度
-
-### 1. 信号识别(最重要)
-
-看推荐词里**出现了什么主题**:
-
-**关键问题:**
-- 哪些推荐词**最接近原始需求**?
-- 哪些推荐词**揭示了有价值的方向**(即使不完全匹配)?
-- 哪些推荐词可以作为**下一层探索的桥梁**?
-- 系统对哪些概念理解得好?哪些理解偏了?
-
-### 2. 组合策略
-
-基于发现的信号,设计下一层探索:
-
-**组合类型:**
-
-a) **关键词直接组合**
-   - 两个关键词组合成新query
-
-b) **利用推荐词作为桥梁**(重要!)
-   - 发现某个推荐词很有价值 → 直接探索这个推荐词
-   - 或在推荐词基础上加其他关键词
-
-c) **跨层级组合**
-   - 结合多层发现的有价值推荐词
-   - 组合成更复杂的query
-
-### 3. 停止条件
-
-**何时可以评估候选?**
-
-满足以下之一:
-- 推荐词中出现了**明确包含原始需求多个核心要素的query**
-- 已经探索到**足够复杂的组合**(3-4个关键词),且推荐词相关
-- 探索了**3-4层**,信息已经足够丰富
-
-**何时继续探索?**
-- 当前推荐词太泛,没有接近原始需求
-- 发现了有价值的信号,但需要进一步组合验证
-- 层数还少(< 3层)
-
-## 输出要求
-
-### 1. key_findings
-总结当前层发现的关键信息,包括:
-- 哪些推荐词最有价值?
-- 系统对哪些概念理解得好/不好?
-- 发现了什么意外的方向?
-
-### 2. promising_signals
-列出最有价值的推荐词(来自任何已探索的query),每个说明为什么有价值
-
-### 3. should_evaluate_now
-是否已经可以开始评估候选了?true/false
-
-### 4. candidates_to_evaluate
-如果should_evaluate_now=true,列出应该评估的候选query
-- 可以是推荐词
-- 可以是自己构造的组合
-
-### 5. next_combinations
-如果should_evaluate_now=false,列出下一层应该探索的query组合
-
-### 6. reasoning
-详细的推理过程
-
-## 重要原则
-
-1. **不要过早评估**:至少探索2层,除非第一层就发现了完美匹配
-2. **充分利用推荐词**:推荐词是系统给的提示,要善用
-3. **保持探索方向的多样性**:不要只盯着一个方向
-4. **识别死胡同**:如果某个方向的推荐词一直不相关,果断放弃
-""".strip()
-
-class PromisingSignal(BaseModel):
-    """有价值的推荐词信号"""
-    query: str = Field(..., description="推荐词")
-    from_level: int = Field(..., description="来自哪一层")
-    reason: str = Field(..., description="为什么有价值")
-
-class LevelAnalysis(BaseModel):
-    """层级分析结果"""
-    key_findings: str = Field(..., description="当前层的关键发现")
-    promising_signals: list[PromisingSignal] = Field(..., description="有价值的推荐词信号")
-    should_evaluate_now: bool = Field(..., description="是否应该开始评估候选")
-    candidates_to_evaluate: list[str] = Field(default_factory=list, description="如果should_evaluate_now=true,要评估的候选query列表")
-    next_combinations: list[str] = Field(default_factory=list, description="如果should_evaluate_now=false,下一层要探索的query组合")
-    reasoning: str = Field(..., description="详细的推理过程")
-
-level_analyzer = Agent[None](
-    name="层级探索分析专家",
-    instructions=level_analysis_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=LevelAnalysis,
-)
-
-
-# ============================================================================
-# Agent 3: 评估专家(简化版:意图匹配 + 相关性评分)
-# ============================================================================
-eval_instructions = """
-你是搜索query评估专家。给定原始问题和推荐query,评估两个维度。
-
-## 评估目标
-
-用这个推荐query搜索,能否找到满足原始需求的内容?
-
-## 两层评分
-
-### 1. intent_match(意图匹配)= true/false
-
-推荐query的**使用意图**是否与原问题一致?
-
-**核心问题:用户搜索这个推荐词,想做什么?**
-
-**判断标准:**
-- 原问题意图:找方法?找教程?找资源/素材?找工具?看作品?
-- 推荐词意图:如果用户搜索这个词,他的目的是什么?
-
-**示例:**
-- 原问题意图="找素材"
-  - ✅ true: "素材下载"、"素材网站"、"免费素材"(都是获取素材)
-  - ❌ false: "素材制作教程"、"如何制作素材"(意图变成学习了)
-
-- 原问题意图="学教程"
-  - ✅ true: "教程视频"、"教学步骤"、"入门指南"
-  - ❌ false: "成品展示"、"作品欣赏"(意图变成看作品了)
-
-**评分:**
-- true = 意图一致,搜索推荐词能达到原问题的目的
-- false = 意图改变,搜索推荐词无法达到原问题的目的
-
-### 2. relevance_score(相关性)= 0-1 连续分数
-
-推荐query在**主题、要素、属性**上与原问题的相关程度?
-
-**评估维度:**
-- 主题相关:核心主题是否匹配?(如:摄影、旅游、美食)
-- 要素覆盖:关键要素保留了多少?(如:地域、时间、对象、工具)
-- 属性匹配:质量、风格、特色等属性是否保留?
-
-**评分参考:**
-- 0.9-1.0 = 几乎完美匹配,所有核心要素都保留
-- 0.7-0.8 = 高度相关,核心要素保留,少数次要要素缺失
-- 0.5-0.6 = 中度相关,主题匹配但多个要素缺失
-- 0.3-0.4 = 低度相关,只有部分主题相关
-- 0-0.2 = 基本不相关
-
-## 评估策略
-
-1. **先判断 intent_match**:意图不匹配直接 false,无论相关性多高
-2. **再评估 relevance_score**:在意图匹配的前提下,计算相关性
-
-## 输出要求
-
-- intent_match: true/false
-- relevance_score: 0-1 的浮点数
-- reason: 详细的评估理由,需要说明:
-  - 原问题的意图是什么
-  - 推荐词的意图是什么
-  - 为什么判断意图匹配/不匹配
-  - 相关性分数的依据(哪些要素保留/缺失)
-""".strip()
-
-class RelevanceEvaluation(BaseModel):
-    """评估反馈模型 - 意图匹配 + 相关性"""
-    intent_match: bool = Field(..., description="意图是否匹配")
-    relevance_score: float = Field(..., description="相关性分数 0-1,分数越高越相关")
-    reason: str = Field(..., description="评估理由,需说明意图判断和相关性依据")
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=RelevanceEvaluation,
-)
-
-
-# ============================================================================
-# Agent 4: 单个帖子需求满足度评估专家
-# ============================================================================
-note_evaluation_instructions = """
-你是帖子需求满足度评估专家。给定原始问题和一个搜索到的帖子(标题+描述),判断这个帖子能否满足用户的需求。
-
-## 你的任务
-
-评估单个帖子的标题和描述,判断用户点开这个帖子后,能否找到满足原始需求的内容。
-
-## 评估维度
-
-### 1. 标题相关性(title_relevance)= 0-1 连续分数
-
-**评估标准:**
-- 标题是否与原始问题的主题相关?
-- 标题是否包含原始问题的关键要素?
-- 标题是否表明内容能解决用户的问题?
-
-**评分参考:**
-- 0.9-1.0 = 标题高度相关,明确表明能解决问题
-- 0.7-0.8 = 标题相关,包含核心要素
-- 0.5-0.6 = 标题部分相关,有关联但不明确
-- 0.3-0.4 = 标题相关性较低
-- 0-0.2 = 标题基本不相关
-
-### 2. 内容预期(content_expectation)= 0-1 连续分数
-
-**评估标准:**
-- 从描述看,内容是否可能包含用户需要的信息?
-- 描述是否展示了相关的要素或细节?
-- 描述的方向是否与用户需求一致?
-
-**评分参考:**
-- 0.9-1.0 = 描述明确表明内容高度符合需求
-- 0.7-0.8 = 描述显示内容可能符合需求
-- 0.5-0.6 = 描述有一定相关性,但不确定
-- 0.3-0.4 = 描述相关性较低
-- 0-0.2 = 描述基本不相关
-
-### 3. 需求满足度(need_satisfaction)= true/false
-
-**核心问题:用户点开这个帖子后,能否找到他需要的内容?**
-
-**判断标准:**
-- 综合标题和描述,内容是否大概率能满足需求?
-- 如果 title_relevance >= 0.7 且 content_expectation >= 0.6,一般判断为 true
-- 否则判断为 false
-
-### 4. 综合置信度(confidence_score)= 0-1 连续分数
-
-**计算方式:**
-- 可以是 title_relevance 和 content_expectation 的加权平均
-- 标题权重通常更高(如 0.6 * title + 0.4 * content)
-
-## 输出要求
-
-- title_relevance: 0-1 的浮点数
-- content_expectation: 0-1 的浮点数
-- need_satisfaction: true/false
-- confidence_score: 0-1 的浮点数
-- reason: 详细的评估理由,需要说明:
-  - 标题与原问题的相关性分析
-  - 描述的内容预期分析
-  - 为什么判断能/不能满足需求
-  - 置信度分数的依据
-
-## 重要原则
-
-1. **独立评估**:只评估这一个帖子,不考虑其他帖子
-2. **用户视角**:问"我会点开这个帖子吗?点开后能找到答案吗?"
-3. **标题优先**:标题是用户决定是否点击的关键
-4. **保守判断**:不确定时,倾向于给较低的分数
-""".strip()
-
-class NoteEvaluation(BaseModel):
-    """单个帖子评估模型"""
-    title_relevance: float = Field(..., description="标题相关性 0-1")
-    content_expectation: float = Field(..., description="内容预期 0-1")
-    need_satisfaction: bool = Field(..., description="是否满足需求")
-    confidence_score: float = Field(..., description="综合置信度 0-1")
-    reason: str = Field(..., description="详细的评估理由")
-
-note_evaluator = Agent[None](
-    name="帖子需求满足度评估专家",
-    instructions=note_evaluation_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=NoteEvaluation,
-)
-
-
-# ============================================================================
-# Agent 5: 答案生成专家
-# ============================================================================
-answer_generation_instructions = """
-你是答案生成专家。基于一组满足需求的帖子,为原始问题生成一个全面、准确、有价值的答案。
-
-## 你的任务
-
-根据用户的原始问题和一组相关帖子(包含标题、描述、置信度评分),生成一个高质量的答案。
-
-## 输入信息
-
-1. **原始问题**:用户提出的具体问题
-2. **相关帖子列表**:每个帖子包含
-   - 序号(索引)
-   - 标题
-   - 描述
-   - 置信度分数
-
-## 答案要求
-
-### 1. 内容要求
-
-- **直接回答问题**:开门见山,第一段就给出核心答案
-- **结构清晰**:使用标题、列表、分段等组织内容
-- **综合多个来源**:整合多个帖子的信息,不要只依赖一个
-- **信息准确**:基于帖子内容,不要编造信息
-- **实用性**:提供可操作的建议或具体的信息
-
-### 2. 引用规范
-
-- **必须标注来源**:每个关键信息都要标注帖子索引
-- **引用格式**:使用 `[1]`、`[2]` 等标注帖子序号
-- **多来源引用**:如果多个帖子支持同一观点,使用 `[1,2,3]`
-- **引用位置**:在相关句子或段落的末尾标注
-
-### 3. 置信度使用
-
-- **优先高置信度**:优先引用置信度高的帖子
-- **交叉验证**:如果多个帖子提到相同信息,可以提高可信度
-- **标注不确定性**:如果信息来自低置信度帖子,适当标注
-
-### 4. 答案结构建议
-
-```
-【核心答案】
-直接回答用户的问题,给出最核心的信息。[引用]
-
-【详细说明】
-1. 第一个方面/要点 [引用]
-   - 具体内容
-   - 相关细节
-
-2. 第二个方面/要点 [引用]
-   - 具体内容
-   - 相关细节
-
-【补充建议/注意事项】(可选)
-其他有价值的信息或提醒。[引用]
-
-【参考帖子】
-列出所有引用的帖子编号和标题。
-```
-
-## 输出格式
-
-{
-  "answer": "生成的答案内容(Markdown格式)",
-  "cited_note_indices": [1, 2, 3],  # 引用的帖子序号列表
-  "confidence": 0.85,  # 答案的整体置信度 (0-1)
-  "summary": "一句话总结答案的核心内容"
-}
-
-## 重要原则
-
-1. **忠于原文**:不要添加帖子中没有的信息
-2. **引用透明**:让用户知道每个信息来自哪个帖子
-3. **综合性**:尽可能整合多个帖子的信息
-4. **可读性**:使用清晰的Markdown格式
-5. **质量优先**:如果帖子质量不够,可以说明信息有限
-""".strip()
-
-class AnswerGeneration(BaseModel):
-    """答案生成模型"""
-    answer: str = Field(..., description="生成的答案内容(Markdown格式)")
-    cited_note_indices: list[int] = Field(..., description="引用的帖子序号列表")
-    confidence: float = Field(..., description="答案的整体置信度 0-1")
-    summary: str = Field(..., description="一句话总结答案的核心内容")
-
-answer_generator = Agent[None](
-    name="答案生成专家",
-    instructions=answer_generation_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=AnswerGeneration,
-)
-
-
-# ============================================================================
-# 日志辅助函数
-# ============================================================================
-
-def add_step(context: RunContext, step_name: str, step_type: str, data: dict):
-    """添加步骤记录"""
-    step = {
-        "step_number": len(context.steps) + 1,
-        "step_name": step_name,
-        "step_type": step_type,
-        "timestamp": datetime.now().isoformat(),
-        "data": data
-    }
-    context.steps.append(step)
-    return step
-
-
-def process_note_data(note: dict) -> dict:
-    """
-    处理搜索接口返回的帖子数据,标准化为统一格式
-
-    Args:
-        note: 搜索接口返回的原始帖子数据
-
-    Returns:
-        标准化后的帖子数据字典
-    """
-    note_card = note.get("note_card", {})
-    image_list = note_card.get("image_list", [])  # 已在搜索API层预处理为URL字符串列表
-    interact_info = note_card.get("interact_info", {})
-    user_info = note_card.get("user", {})
-
-    return {
-        "note_id": note.get("id", ""),
-        "title": note_card.get("display_title", ""),
-        "desc": note_card.get("desc", ""),
-        "image_list": image_list,  # 第一张就是封面,已在XiaohongshuSearch.search()中预处理为URL字符串列表
-        "interact_info": {
-            "liked_count": interact_info.get("liked_count", 0),
-            "collected_count": interact_info.get("collected_count", 0),
-            "comment_count": interact_info.get("comment_count", 0),
-            "shared_count": interact_info.get("shared_count", 0)
-        },
-        "user": {
-            "nickname": user_info.get("nickname", ""),
-            "user_id": user_info.get("user_id", "")
-        },
-        "type": note_card.get("type", "normal"),
-        "note_url": f"https://www.xiaohongshu.com/explore/{note.get('id', '')}"
-    }
-
-
-# ============================================================================
-# 核心函数
-# ============================================================================
-
-async def extract_keywords(q: str, context: RunContext) -> KeywordList:
-    """提取关键词"""
-    print("\n[步骤 1] 正在提取关键词...")
-    result = await Runner.run(keyword_extractor, q)
-    keyword_list: KeywordList = result.final_output
-    print(f"提取的关键词:{keyword_list.keywords}")
-    print(f"提取理由:{keyword_list.reasoning}")
-
-    # 记录步骤
-    add_step(context, "提取关键词", "keyword_extraction", {
-        "input_question": q,
-        "keywords": keyword_list.keywords,
-        "reasoning": keyword_list.reasoning
-    })
-
-    return keyword_list
-
-
-async def explore_level(queries: list[str], level_num: int, context: RunContext) -> dict:
-    """探索一个层级(并发获取所有query的推荐词)"""
-    step_num = len(context.steps) + 1
-    print(f"\n{'='*60}")
-    print(f"[步骤 {step_num}] Level {level_num} 探索:{len(queries)} 个query")
-    print(f"{'='*60}")
-
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-
-    # 并发获取所有推荐词
-    async def get_single_sug(query: str):
-        print(f"  探索: {query}")
-        suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-        print(f"    → {len(suggestions) if suggestions else 0} 个推荐词")
-        return {
-            "query": query,
-            "suggestions": suggestions or []
-        }
-
-    results = await asyncio.gather(*[get_single_sug(q) for q in queries])
-
-    level_data = {
-        "level": level_num,
-        "timestamp": datetime.now().isoformat(),
-        "queries": results
-    }
-
-    context.exploration_levels.append(level_data)
-
-    # 记录步骤
-    add_step(context, f"Level {level_num} 探索", "level_exploration", {
-        "level": level_num,
-        "input_queries": queries,
-        "query_count": len(queries),
-        "results": results,
-        "total_suggestions": sum(len(r['suggestions']) for r in results)
-    })
-
-    return level_data
-
-
-async def analyze_level(level_data: dict, all_levels: list[dict], original_question: str, context: RunContext) -> LevelAnalysis:
-    """分析当前层级,决定下一步"""
-    step_num = len(context.steps) + 1
-    print(f"\n[步骤 {step_num}] 正在分析 Level {level_data['level']}...")
-
-    # 构造输入
-    analysis_input = f"""
-<原始问题>
-{original_question}
-</原始问题>
-
-<已探索的所有层级>
-{json.dumps(all_levels, ensure_ascii=False, indent=2)}
-</已探索的所有层级>
-
-<当前层级>
-Level {level_data['level']}
-{json.dumps(level_data['queries'], ensure_ascii=False, indent=2)}
-</当前层级>
-
-请分析当前探索状态,决定下一步行动。
-"""
-
-    result = await Runner.run(level_analyzer, analysis_input)
-    analysis: LevelAnalysis = result.final_output
-
-    print(f"\n分析结果:")
-    print(f"  关键发现:{analysis.key_findings}")
-    print(f"  有价值的信号:{len(analysis.promising_signals)} 个")
-    print(f"  是否评估:{analysis.should_evaluate_now}")
-
-    if analysis.should_evaluate_now:
-        print(f"  候选query:{analysis.candidates_to_evaluate}")
-    else:
-        print(f"  下一层探索:{analysis.next_combinations}")
-
-    # 保存分析结果
-    context.level_analyses.append({
-        "level": level_data['level'],
-        "timestamp": datetime.now().isoformat(),
-        "analysis": analysis.model_dump()
-    })
-
-    # 记录步骤
-    add_step(context, f"Level {level_data['level']} 分析", "level_analysis", {
-        "level": level_data['level'],
-        "key_findings": analysis.key_findings,
-        "promising_signals_count": len(analysis.promising_signals),
-        "promising_signals": [s.model_dump() for s in analysis.promising_signals],
-        "should_evaluate_now": analysis.should_evaluate_now,
-        "candidates_to_evaluate": analysis.candidates_to_evaluate if analysis.should_evaluate_now else [],
-        "next_combinations": analysis.next_combinations if not analysis.should_evaluate_now else [],
-        "reasoning": analysis.reasoning
-    })
-
-    return analysis
-
-
-async def evaluate_candidates(candidates: list[str], original_question: str, context: RunContext) -> list[dict]:
-    """评估候选query(含实际搜索验证)"""
-    step_num = len(context.steps) + 1
-    print(f"\n{'='*60}")
-    print(f"[步骤 {step_num}] 评估 {len(candidates)} 个候选query")
-    print(f"{'='*60}")
-
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-    xiaohongshu_search = XiaohongshuSearch()
-
-    # 创建搜索结果保存目录
-    search_results_dir = os.path.join(context.log_dir, "search_results")
-    os.makedirs(search_results_dir, exist_ok=True)
-
-    async def evaluate_single_candidate(candidate: str, candidate_index: int):
-        print(f"\n评估候选:{candidate}")
-
-        # 为当前候选创建子目录
-        # 清理候选名称,移除不适合作为目录名的字符
-        safe_candidate_name = "".join(c if c.isalnum() or c in (' ', '_', '-') else '_' for c in candidate)
-        candidate_dir = os.path.join(search_results_dir, f"candidate_{candidate_index+1}_{safe_candidate_name[:50]}")
-        os.makedirs(candidate_dir, exist_ok=True)
-
-        # 1. 获取推荐词
-        suggestions = xiaohongshu_api.get_recommendations(keyword=candidate)
-        print(f"  获取到 {len(suggestions) if suggestions else 0} 个推荐词")
-
-        if not suggestions:
-            return {
-                "candidate": candidate,
-                "suggestions": [],
-                "evaluations": []
-            }
-
-        # 2. 评估每个推荐词(意图匹配 + 相关性)
-        async def eval_single_sug(sug: str, sug_index: int):
-            # 2.1 先进行意图和相关性评估
-            eval_input = f"""
-<原始问题>
-{original_question}
-</原始问题>
-
-<待评估的推荐query>
-{sug}
-</待评估的推荐query>
-
-请评估该推荐query:
-1. intent_match: 意图是否匹配(true/false)
-2. relevance_score: 相关性分数(0-1)
-3. reason: 详细的评估理由
-"""
-            result = await Runner.run(evaluator, eval_input)
-            evaluation: RelevanceEvaluation = result.final_output
-
-            eval_result = {
-                "query": sug,
-                "intent_match": evaluation.intent_match,
-                "relevance_score": evaluation.relevance_score,
-                "reason": evaluation.reason,
-            }
-
-            # 2.2 如果意图匹配且相关性足够高,进行实际搜索验证
-            if evaluation.intent_match and evaluation.relevance_score >= 0.7:
-                print(f"    → 合格候选,进行实际搜索验证: {sug}")
-                try:
-                    search_result = xiaohongshu_search.search(keyword=sug)
-
-                    # 解析result字段(它是JSON字符串,需要先解析)
-                    result_str = search_result.get("result", "{}")
-                    if isinstance(result_str, str):
-                        result_data = json.loads(result_str)
-                    else:
-                        result_data = result_str
-
-                    # 格式化搜索结果:将result字段解析后再保存
-                    formatted_search_result = {
-                        "success": search_result.get("success"),
-                        "result": result_data,  # 保存解析后的数据
-                        "tool_name": search_result.get("tool_name"),
-                        "call_type": search_result.get("call_type"),
-                        "query": sug,
-                        "timestamp": datetime.now().isoformat()
-                    }
-
-                    # 保存格式化后的搜索结果到文件
-                    safe_sug_name = "".join(c if c.isalnum() or c in (' ', '_', '-') else '_' for c in sug)
-                    search_result_file = os.path.join(candidate_dir, f"sug_{sug_index+1}_{safe_sug_name[:30]}.json")
-                    with open(search_result_file, 'w', encoding='utf-8') as f:
-                        json.dump(formatted_search_result, f, ensure_ascii=False, indent=2)
-                    print(f"       搜索结果已保存: {os.path.basename(search_result_file)}")
-
-                    # 提取搜索结果的标题和描述
-                    # 正确的数据路径: result.data.data[]
-                    notes = result_data.get("data", {}).get("data", [])
-                    if notes:
-                        print(f"       开始评估 {len(notes)} 个帖子...")
-
-                        # 对每个帖子进行独立评估
-                        note_evaluations = []
-                        for note_idx, note in enumerate(notes, 1):  # 评估所有帖子
-                            note_card = note.get("note_card", {})
-                            title = note_card.get("display_title", "")
-                            desc = note_card.get("desc", "")
-                            note_id = note.get("id", "")
-
-                            # 构造评估输入
-                            eval_input = f"""
-<原始问题>
-{original_question}
-</原始问题>
-
-<帖子信息>
-标题: {title}
-描述: {desc}
-</帖子信息>
-
-请评估这个帖子能否满足用户需求。
-"""
-                            # 调用评估Agent
-                            eval_result_run = await Runner.run(note_evaluator, eval_input)
-                            note_eval: NoteEvaluation = eval_result_run.final_output
-
-                            note_evaluation_record = {
-                                "note_index": note_idx,
-                                "note_id": note_id,
-                                "title": title,
-                                "desc": desc,  # 保存完整描述
-                                "evaluation": {
-                                    "title_relevance": note_eval.title_relevance,
-                                    "content_expectation": note_eval.content_expectation,
-                                    "need_satisfaction": note_eval.need_satisfaction,
-                                    "confidence_score": note_eval.confidence_score,
-                                    "reason": note_eval.reason
-                                }
-                            }
-                            note_evaluations.append(note_evaluation_record)
-
-                            # 简单打印进度
-                            if note_idx % 3 == 0 or note_idx == len(notes):
-                                print(f"         已评估 {note_idx}/{len(notes)} 个帖子")
-
-                        # 统计满足需求的帖子数量
-                        satisfied_count = sum(1 for ne in note_evaluations if ne["evaluation"]["need_satisfaction"])
-                        avg_confidence = sum(ne["evaluation"]["confidence_score"] for ne in note_evaluations) / len(note_evaluations) if note_evaluations else 0
-
-                        eval_result["search_verification"] = {
-                            "total_notes": len(notes),
-                            "evaluated_notes": len(note_evaluations),
-                            "satisfied_count": satisfied_count,
-                            "average_confidence": round(avg_confidence, 2),
-                            "note_evaluations": note_evaluations,
-                            "search_result_file": search_result_file
-                        }
-
-                        print(f"       评估完成: {satisfied_count}/{len(note_evaluations)} 个帖子满足需求, "
-                              f"平均置信度={avg_confidence:.2f}")
-                    else:
-                        eval_result["search_verification"] = {
-                            "total_notes": 0,
-                            "evaluated_notes": 0,
-                            "satisfied_count": 0,
-                            "average_confidence": 0.0,
-                            "note_evaluations": [],
-                            "search_result_file": search_result_file,
-                            "reason": "搜索无结果"
-                        }
-                        print(f"       搜索无结果")
-
-                except Exception as e:
-                    print(f"       搜索验证出错: {e}")
-                    eval_result["search_verification"] = {
-                        "error": str(e)
-                    }
-
-            return eval_result
-
-        evaluations = await asyncio.gather(*[eval_single_sug(s, i) for i, s in enumerate(suggestions)])
-
-        return {
-            "candidate": candidate,
-            "suggestions": suggestions,
-            "evaluations": evaluations
-        }
-
-    results = await asyncio.gather(*[evaluate_single_candidate(c, i) for i, c in enumerate(candidates)])
-
-    # 生成搜索结果汇总文件
-    summary_data = {
-        "original_question": original_question,
-        "timestamp": datetime.now().isoformat(),
-        "total_candidates": len(candidates),
-        "candidates": []
-    }
-
-    for i, result in enumerate(results):
-        candidate_summary = {
-            "index": i + 1,
-            "candidate": result["candidate"],
-            "suggestions_count": len(result["suggestions"]),
-            "verified_queries": []
-        }
-
-        for eval_item in result.get("evaluations", []):
-            if "search_verification" in eval_item and "search_result_file" in eval_item["search_verification"]:
-                sv = eval_item["search_verification"]
-                candidate_summary["verified_queries"].append({
-                    "query": eval_item["query"],
-                    "intent_match": eval_item["intent_match"],
-                    "relevance_score": eval_item["relevance_score"],
-                    "verification": {
-                        "total_notes": sv.get("total_notes", 0),
-                        "evaluated_notes": sv.get("evaluated_notes", 0),
-                        "satisfied_count": sv.get("satisfied_count", 0),
-                        "average_confidence": sv.get("average_confidence", 0.0)
-                    },
-                    "search_result_file": sv["search_result_file"]
-                })
-
-        summary_data["candidates"].append(candidate_summary)
-
-    # 保存汇总文件
-    summary_file = os.path.join(search_results_dir, "summary.json")
-    with open(summary_file, 'w', encoding='utf-8') as f:
-        json.dump(summary_data, f, ensure_ascii=False, indent=2)
-    print(f"\n搜索结果汇总已保存: {summary_file}")
-
-    context.evaluation_results = results
-
-    # 构建详细的步骤记录数据
-    step_data = {
-        "candidate_count": len(candidates),
-        "candidates": candidates,
-        "total_evaluations": sum(len(r['evaluations']) for r in results),
-        "verified_queries": sum(
-            1 for r in results
-            for e in r.get('evaluations', [])
-            if 'search_verification' in e
-        ),
-        "search_results_dir": search_results_dir,
-        "summary_file": summary_file,
-        "detailed_results": []
-    }
-
-    # 为每个候选记录详细信息
-    for result in results:
-        candidate_detail = {
-            "candidate": result["candidate"],
-            "suggestions": result["suggestions"],
-            "evaluations": []
-        }
-
-        for eval_item in result.get("evaluations", []):
-            eval_detail = {
-                "query": eval_item["query"],
-                "intent_match": eval_item["intent_match"],
-                "relevance_score": eval_item["relevance_score"],
-                "reason": eval_item["reason"]
-            }
-
-            # 如果有搜索验证,添加详细信息
-            if "search_verification" in eval_item:
-                verification = eval_item["search_verification"]
-                eval_detail["search_verification"] = {
-                    "performed": True,
-                    "total_notes": verification.get("total_notes", 0),
-                    "evaluated_notes": verification.get("evaluated_notes", 0),
-                    "satisfied_count": verification.get("satisfied_count", 0),
-                    "average_confidence": verification.get("average_confidence", 0.0),
-                    "search_result_file": verification.get("search_result_file"),
-                    "has_error": "error" in verification
-                }
-                if "error" in verification:
-                    eval_detail["search_verification"]["error"] = verification["error"]
-
-                # 保存每个帖子的评估详情
-                if "note_evaluations" in verification:
-                    eval_detail["search_verification"]["note_evaluations"] = verification["note_evaluations"]
-            else:
-                eval_detail["search_verification"] = {
-                    "performed": False,
-                    "reason": "未达到搜索验证阈值(intent_match=False 或 relevance_score<0.7)"
-                }
-
-            candidate_detail["evaluations"].append(eval_detail)
-
-        step_data["detailed_results"].append(candidate_detail)
-
-    # 记录步骤
-    add_step(context, "评估候选query", "candidate_evaluation", step_data)
-
-    return results
-
-
-# ============================================================================
-# 新的独立步骤函数(方案A)
-# ============================================================================
-
-async def step_evaluate_query_suggestions(candidates: list[str], original_question: str, context: RunContext) -> list[dict]:
-    """
-    步骤1: 评估候选query的推荐词
-
-    输入:
-    - candidates: 候选query列表
-    - original_question: 原始问题
-    - context: 运行上下文
-
-    输出:
-    - 每个候选的评估结果列表,包含:
-      - candidate: 候选query
-      - suggestions: 推荐词列表
-      - evaluations: 每个推荐词的意图匹配和相关性评分
-    """
-    step_num = len(context.steps) + 1
-    print(f"\n{'='*60}")
-    print(f"[步骤 {step_num}] 评估 {len(candidates)} 个候选query的推荐词")
-    print(f"{'='*60}")
-
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-
-    async def evaluate_single_candidate(candidate: str):
-        print(f"\n评估候选:{candidate}")
-
-        # 1. 获取推荐词
-        suggestions = xiaohongshu_api.get_recommendations(keyword=candidate)
-        print(f"  获取到 {len(suggestions) if suggestions else 0} 个推荐词")
-
-        if not suggestions:
-            return {
-                "candidate": candidate,
-                "suggestions": [],
-                "evaluations": []
-            }
-
-        # 2. 评估每个推荐词(只做意图匹配和相关性评分)
-        async def eval_single_sug(sug: str):
-            eval_input = f"""
-<原始问题>
-{original_question}
-</原始问题>
-
-<待评估的推荐query>
-{sug}
-</待评估的推荐query>
-
-请评估该推荐query:
-1. intent_match: 意图是否匹配(true/false)
-2. relevance_score: 相关性分数(0-1)
-3. reason: 详细的评估理由
-"""
-            result = await Runner.run(evaluator, eval_input)
-            evaluation: RelevanceEvaluation = result.final_output
-
-            return {
-                "query": sug,
-                "intent_match": evaluation.intent_match,
-                "relevance_score": evaluation.relevance_score,
-                "reason": evaluation.reason
-            }
-
-        evaluations = await asyncio.gather(*[eval_single_sug(s) for s in suggestions])
-
-        return {
-            "candidate": candidate,
-            "suggestions": suggestions,
-            "evaluations": evaluations
-        }
-
-    results = await asyncio.gather(*[evaluate_single_candidate(c) for c in candidates])
-
-    # 记录步骤
-    add_step(context, "评估候选query的推荐词", "query_suggestion_evaluation", {
-        "candidate_count": len(candidates),
-        "candidates": candidates,
-        "results": results,
-        "total_evaluations": sum(len(r['evaluations']) for r in results),
-        "qualified_count": sum(
-            1 for r in results
-            for e in r['evaluations']
-            if e['intent_match'] and e['relevance_score'] >= 0.7
-        )
-    })
-
-    return results
-
-
-def step_filter_qualified_queries(evaluation_results: list[dict], context: RunContext, min_relevance_score: float = 0.7) -> list[dict]:
-    """
-    步骤1.5: 筛选合格的推荐词
-
-    输入:
-    - evaluation_results: 步骤1的评估结果
-
-    输出:
-    - 合格的query列表,每个包含:
-      - query: 推荐词
-      - from_candidate: 来源候选
-      - intent_match: 意图匹配
-      - relevance_score: 相关性分数
-      - reason: 评估理由
-    """
-    step_num = len(context.steps) + 1
-    print(f"\n{'='*60}")
-    print(f"[步骤 {step_num}] 筛选合格的推荐词")
-    print(f"{'='*60}")
-
-    qualified_queries = []
-    all_queries = []  # 保存所有查询,包括不合格的
-
-    for result in evaluation_results:
-        candidate = result["candidate"]
-        for eval_item in result.get("evaluations", []):
-            query_data = {
-                "query": eval_item["query"],
-                "from_candidate": candidate,
-                "intent_match": eval_item["intent_match"],
-                "relevance_score": eval_item["relevance_score"],
-                "reason": eval_item["reason"]
-            }
-
-            # 判断是否合格
-            is_qualified = (eval_item['intent_match'] is True
-                          and eval_item['relevance_score'] >= min_relevance_score)
-            query_data["is_qualified"] = is_qualified
-
-            all_queries.append(query_data)
-            if is_qualified:
-                qualified_queries.append(query_data)
-
-    # 按相关性分数降序排列
-    qualified_queries.sort(key=lambda x: x['relevance_score'], reverse=True)
-    all_queries.sort(key=lambda x: x['relevance_score'], reverse=True)
-
-    print(f"\n找到 {len(qualified_queries)} 个合格的推荐词 (共评估 {len(all_queries)} 个)")
-    if qualified_queries:
-        print(f"相关性分数范围: {qualified_queries[0]['relevance_score']:.2f} ~ {qualified_queries[-1]['relevance_score']:.2f}")
-        print("\n合格的推荐词:")
-        for idx, q in enumerate(qualified_queries[:5], 1):
-            print(f"  {idx}. {q['query']} (分数: {q['relevance_score']:.2f})")
-        if len(qualified_queries) > 5:
-            print(f"  ... 还有 {len(qualified_queries) - 5} 个")
-
-    # 记录步骤 - 保存所有查询数据
-    add_step(context, "筛选合格的推荐词", "filter_qualified_queries", {
-        "input_evaluation_count": len(all_queries),
-        "min_relevance_score": min_relevance_score,
-        "qualified_count": len(qualified_queries),
-        "qualified_queries": qualified_queries,
-        "all_queries": all_queries  # 新增:保存所有查询数据
-    })
-
-    return qualified_queries
-
-
-async def step_search_qualified_queries(qualified_queries: list[dict], context: RunContext) -> dict:
-    """
-    步骤2: 搜索合格的推荐词
-
-    输入:
-    - qualified_queries: 步骤1.5筛选出的合格query列表,每个包含:
-      - query: 推荐词
-      - from_candidate: 来源候选
-      - intent_match, relevance_score, reason
-
-    输出:
-    - 搜索结果字典,包含:
-      - searches: 每个query的搜索结果列表
-      - search_results_dir: 搜索结果保存目录
-    """
-    step_num = len(context.steps) + 1
-    print(f"\n{'='*60}")
-    print(f"[步骤 {step_num}] 搜索 {len(qualified_queries)} 个合格的推荐词")
-    print(f"{'='*60}")
-
-    if not qualified_queries:
-        add_step(context, "搜索合格的推荐词", "search_qualified_queries", {
-            "qualified_count": 0,
-            "searches": []
-        })
-        return {"searches": [], "search_results_dir": None}
-
-    # 创建搜索结果保存目录
-    search_results_dir = os.path.join(context.log_dir, "search_results")
-    os.makedirs(search_results_dir, exist_ok=True)
-
-    xiaohongshu_search = XiaohongshuSearch()
-
-    # 搜索每个合格的query
-    async def search_single_query(query_info: dict, query_index: int):
-        query = query_info['query']
-        print(f"\n搜索 [{query_index+1}/{len(qualified_queries)}]: {query}")
-
-        try:
-            # 执行搜索
-            search_result = xiaohongshu_search.search(keyword=query)
-
-            # 解析result字段
-            result_str = search_result.get("result", "{}")
-            if isinstance(result_str, str):
-                result_data = json.loads(result_str)
-            else:
-                result_data = result_str
-
-            # 格式化搜索结果
-            formatted_search_result = {
-                "success": search_result.get("success"),
-                "result": result_data,
-                "tool_name": search_result.get("tool_name"),
-                "call_type": search_result.get("call_type"),
-                "query": query,
-                "timestamp": datetime.now().isoformat()
-            }
-
-            # 保存到文件
-            safe_query_name = "".join(c if c.isalnum() or c in (' ', '_', '-') else '_' for c in query)
-            query_dir = os.path.join(search_results_dir, f"query_{query_index+1}_{safe_query_name[:50]}")
-            os.makedirs(query_dir, exist_ok=True)
-
-            search_result_file = os.path.join(query_dir, "search_result.json")
-            with open(search_result_file, 'w', encoding='utf-8') as f:
-                json.dump(formatted_search_result, f, ensure_ascii=False, indent=2)
-
-            # 提取帖子列表
-            notes = result_data.get("data", {}).get("data", [])
-
-            print(f"  → 搜索成功,获得 {len(notes)} 个帖子")
-
-            # ⭐ 提取帖子摘要信息用于steps.json
-            notes_summary = [process_note_data(note) for note in notes]
-
-            return {
-                "query": query,
-                "from_candidate": query_info['from_candidate'],
-                "intent_match": query_info['intent_match'],
-                "relevance_score": query_info['relevance_score'],
-                "reason": query_info['reason'],
-                "search_result_file": search_result_file,
-                "note_count": len(notes),
-                "notes": notes,  # 保存所有帖子用于评估
-                "notes_summary": notes_summary  # ⭐ 保存到steps.json
-            }
-
-        except Exception as e:
-            print(f"  → 搜索失败: {e}")
-            return {
-                "query": query,
-                "from_candidate": query_info['from_candidate'],
-                "intent_match": query_info['intent_match'],
-                "relevance_score": query_info['relevance_score'],
-                "reason": query_info['reason'],
-                "error": str(e),
-                "note_count": 0,
-                "notes": []
-            }
-
-    search_results = await asyncio.gather(*[search_single_query(q, i) for i, q in enumerate(qualified_queries)])
-
-    # 记录步骤
-    add_step(context, "搜索合格的推荐词", "search_qualified_queries", {
-        "qualified_count": len(qualified_queries),
-        "search_results": [
-            {
-                "query": sr['query'],
-                "from_candidate": sr['from_candidate'],
-                "note_count": sr['note_count'],
-                "search_result_file": sr.get('search_result_file'),
-                "has_error": 'error' in sr,
-                "notes_summary": sr.get('notes_summary', [])  # ⭐ 包含帖子摘要
-            }
-            for sr in search_results
-        ],
-        "search_results_dir": search_results_dir
-    })
-
-    return {
-        "searches": search_results,
-        "search_results_dir": search_results_dir
-    }
-
-
-async def step_evaluate_search_notes(search_data: dict, original_question: str, context: RunContext) -> dict:
-    """
-    步骤3: 评估搜索到的帖子
-
-    输入:
-    - search_data: 步骤2的搜索结果,包含:
-      - searches: 搜索结果列表
-      - search_results_dir: 结果目录
-
-    输出:
-    - 帖子评估结果字典,包含:
-      - note_evaluations: 每个query的帖子评估列表
-    """
-    step_num = len(context.steps) + 1
-    print(f"\n{'='*60}")
-    print(f"[步骤 {step_num}] 评估搜索到的帖子")
-    print(f"{'='*60}")
-
-    search_results = search_data['searches']
-
-    if not search_results:
-        add_step(context, "评估搜索到的帖子", "evaluate_search_notes", {
-            "query_count": 0,
-            "total_notes": 0,
-            "evaluated_notes": 0,
-            "note_evaluations": []
-        })
-        return {"note_evaluations": []}
-
-    # 对每个query的帖子进行评估
-    async def evaluate_query_notes(search_result: dict, query_index: int):
-        query = search_result['query']
-        notes = search_result.get('notes', [])
-
-        if not notes or 'error' in search_result:
-            return {
-                "query": query,
-                "from_candidate": search_result['from_candidate'],
-                "note_count": 0,
-                "evaluated_notes": [],
-                "satisfied_count": 0,
-                "average_confidence": 0.0
-            }
-
-        print(f"\n评估query [{query_index+1}]: {query} ({len(notes)} 个帖子)")
-
-        # 评估每个帖子
-        note_evaluations = []
-        for note_idx, note in enumerate(notes, 1):
-            # 使用标准化函数处理帖子数据
-            note_data = process_note_data(note)
-            title = note_data["title"]
-            desc = note_data["desc"]
-
-            # 调用评估Agent
-            eval_input = f"""
-<原始问题>
-{original_question}
-</原始问题>
-
-<帖子信息>
-标题: {title}
-描述: {desc}
-</帖子信息>
-
-请评估这个帖子能否满足用户需求。
-"""
-            eval_result_run = await Runner.run(note_evaluator, eval_input)
-            note_eval: NoteEvaluation = eval_result_run.final_output
-
-            # 合并标准化的帖子数据和评估结果
-            note_evaluations.append({
-                **note_data,  # 包含所有标准化字段
-                "note_index": note_idx,
-                "evaluation": {
-                    "title_relevance": note_eval.title_relevance,
-                    "content_expectation": note_eval.content_expectation,
-                    "need_satisfaction": note_eval.need_satisfaction,
-                    "confidence_score": note_eval.confidence_score,
-                    "reason": note_eval.reason
-                }
-            })
-
-            if note_idx % 3 == 0 or note_idx == len(notes):
-                print(f"  已评估 {note_idx}/{len(notes)} 个帖子")
-
-        # 统计
-        satisfied_count = sum(1 for ne in note_evaluations if ne["evaluation"]["need_satisfaction"])
-        avg_confidence = sum(ne["evaluation"]["confidence_score"] for ne in note_evaluations) / len(note_evaluations) if note_evaluations else 0
-
-        print(f"  → 完成:{satisfied_count}/{len(note_evaluations)} 个帖子满足需求")
-
-        return {
-            "query": query,
-            "from_candidate": search_result['from_candidate'],
-            "note_count": len(notes),
-            "evaluated_notes": note_evaluations,
-            "satisfied_count": satisfied_count,
-            "average_confidence": round(avg_confidence, 2)
-        }
-
-    # 并发评估所有query的帖子
-    all_evaluations = await asyncio.gather(*[evaluate_query_notes(sr, i) for i, sr in enumerate(search_results, 1)])
-
-    # 记录步骤
-    total_notes = sum(e['note_count'] for e in all_evaluations)
-    total_satisfied = sum(e['satisfied_count'] for e in all_evaluations)
-
-    add_step(context, "评估搜索到的帖子", "evaluate_search_notes", {
-        "query_count": len(search_results),
-        "total_notes": total_notes,
-        "total_satisfied": total_satisfied,
-        "note_evaluations": all_evaluations
-    })
-
-    return {"note_evaluations": all_evaluations}
-
-
-def step_collect_satisfied_notes(note_evaluation_data: dict) -> list[dict]:
-    """
-    步骤4: 汇总所有满足需求的帖子
-
-    输入:
-    - note_evaluation_data: 步骤3的帖子评估结果
-
-    输出:
-    - 所有满足需求的帖子列表,按置信度降序排列
-    """
-    print(f"\n{'='*60}")
-    print(f"汇总满足需求的帖子")
-    print(f"{'='*60}")
-
-    all_satisfied_notes = []
-
-    for query_eval in note_evaluation_data['note_evaluations']:
-        for note in query_eval['evaluated_notes']:
-            if note['evaluation']['need_satisfaction']:
-                all_satisfied_notes.append({
-                    "query": query_eval['query'],
-                    "from_candidate": query_eval['from_candidate'],
-                    "note_id": note['note_id'],
-                    "title": note['title'],
-                    "desc": note['desc'],
-                    # ⭐ 保留完整帖子信息
-                    "image_list": note.get('image_list', []),
-                    "cover_image": note.get('cover_image', {}),
-                    "interact_info": note.get('interact_info', {}),
-                    "user": note.get('user', {}),
-                    "type": note.get('type', 'normal'),
-                    "note_url": note.get('note_url', ''),
-                    # 评估结果
-                    "title_relevance": note['evaluation']['title_relevance'],
-                    "content_expectation": note['evaluation']['content_expectation'],
-                    "confidence_score": note['evaluation']['confidence_score'],
-                    "reason": note['evaluation']['reason']
-                })
-
-    # 按置信度降序排列
-    all_satisfied_notes.sort(key=lambda x: x['confidence_score'], reverse=True)
-
-    print(f"\n共收集到 {len(all_satisfied_notes)} 个满足需求的帖子")
-    if all_satisfied_notes:
-        print(f"置信度范围: {all_satisfied_notes[0]['confidence_score']:.2f} ~ {all_satisfied_notes[-1]['confidence_score']:.2f}")
-
-    return all_satisfied_notes
-
-
-async def step_generate_answer(satisfied_notes: list[dict], original_question: str, context: RunContext) -> dict:
-    """
-    步骤5: 基于满足需求的帖子生成答案
-
-    输入:
-    - satisfied_notes: 步骤4收集的满足需求的帖子列表
-    - original_question: 原始问题
-    - context: 运行上下文
-
-    输出:
-    - 生成的答案及相关信息
-      - answer: 答案内容(Markdown格式)
-      - cited_note_indices: 引用的帖子索引
-      - confidence: 答案置信度
-      - summary: 答案摘要
-      - cited_notes: 被引用的帖子详情
-    """
-    step_num = len(context.steps) + 1
-    print(f"\n{'='*60}")
-    print(f"[步骤 {step_num}] 基于 {len(satisfied_notes)} 个帖子生成答案")
-    print(f"{'='*60}")
-
-    if not satisfied_notes:
-        print("\n⚠️  没有满足需求的帖子,无法生成答案")
-        result = {
-            "answer": "抱歉,未找到能够回答该问题的相关内容。",
-            "cited_note_indices": [],
-            "confidence": 0.0,
-            "summary": "无可用信息",
-            "cited_notes": []
-        }
-
-        add_step(context, "生成答案", "answer_generation", {
-            "original_question": original_question,
-            "input_notes_count": 0,
-            "result": result
-        })
-
-        return result
-
-    # 构建Agent输入
-    notes_info = []
-    for idx, note in enumerate(satisfied_notes, 1):
-        notes_info.append(f"""
-【帖子 {idx}】
-标题: {note['title']}
-描述: {note['desc']}
-置信度: {note['confidence_score']:.2f}
-""".strip())
-
-    agent_input = f"""
-<原始问题>
-{original_question}
-</原始问题>
-
-<相关帖子>
-{chr(10).join(notes_info)}
-</相关帖子>
-
-请基于以上帖子,为原始问题生成一个全面、准确的答案。
-记得在答案中使用 [1], [2] 等标注引用的帖子序号。
-""".strip()
-
-    print(f"\n📝 调用答案生成Agent...")
-    print(f"  - 可用帖子: {len(satisfied_notes)} 个")
-    print(f"  - 平均置信度: {sum(n['confidence_score'] for n in satisfied_notes) / len(satisfied_notes):.2f}")
-
-    # 调用Agent生成答案
-    result_run = await Runner.run(answer_generator, agent_input)
-    answer_result: AnswerGeneration = result_run.final_output
-
-    # 提取被引用的帖子详情
-    cited_notes = []
-    for idx in answer_result.cited_note_indices:
-        if 1 <= idx <= len(satisfied_notes):
-            note = satisfied_notes[idx - 1]
-            cited_notes.append({
-                "index": idx,
-                "note_id": note['note_id'],
-                "title": note['title'],
-                "desc": note['desc'],
-                "confidence_score": note['confidence_score'],
-                # ⭐ 完整帖子信息用于可视化
-                "image_list": note.get('image_list', []),
-                "cover_image": note.get('cover_image', {}),
-                "interact_info": note.get('interact_info', {}),
-                "user": note.get('user', {}),
-                "note_url": note.get('note_url', ''),
-                "type": note.get('type', 'normal'),
-                # ⭐ 评估详情
-                "title_relevance": note.get('title_relevance', 0),
-                "content_expectation": note.get('content_expectation', 0),
-                "reason": note.get('reason', '')
-            })
-
-    result = {
-        "answer": answer_result.answer,
-        "cited_note_indices": answer_result.cited_note_indices,
-        "confidence": answer_result.confidence,
-        "summary": answer_result.summary,
-        "cited_notes": cited_notes
-    }
-
-    # 打印结果
-    print(f"\n✅ 答案生成完成")
-    print(f"  - 引用帖子数: {len(answer_result.cited_note_indices)} 个")
-    print(f"  - 答案置信度: {answer_result.confidence:.2f}")
-    print(f"  - 答案摘要: {answer_result.summary}")
-
-    # 记录步骤
-    add_step(context, "生成答案", "answer_generation", {
-        "original_question": original_question,
-        "input_notes_count": len(satisfied_notes),
-        "result": result,
-        "agent_input_preview": agent_input[:500] + "..." if len(agent_input) > 500 else agent_input
-    })
-
-    return result
-
-
-def find_qualified_queries(evaluation_results: list[dict], min_relevance_score: float = 0.7) -> list[dict]:
-    """
-    查找所有合格的query(旧函数,保留兼容性)
-
-    筛选标准:
-    1. intent_match = True(必须满足)
-    2. relevance_score >= min_relevance_score
-
-    返回:按 relevance_score 降序排列
-    """
-    all_qualified = []
-
-    for result in evaluation_results:
-        for eval_item in result.get("evaluations", []):
-            if (eval_item['intent_match'] is True
-                and eval_item['relevance_score'] >= min_relevance_score):
-                all_qualified.append({
-                    "from_candidate": result["candidate"],
-                    **eval_item
-                })
-
-    # 按relevance_score降序排列
-    return sorted(all_qualified, key=lambda x: x['relevance_score'], reverse=True)
-
-
-# ============================================================================
-# 主流程
-# ============================================================================
-
-async def progressive_exploration(context: RunContext, max_levels: int = 4) -> dict:
-    """
-    渐进式探索流程 - 使用独立步骤
-
-    流程:
-    1. 提取关键词 + 渐进式探索(复用旧流程)
-    2. 步骤1: 评估候选query的推荐词
-    3. 步骤2: 搜索合格的推荐词
-    4. 步骤3: 评估搜索到的帖子
-    5. 步骤4: 汇总满足需求的帖子
-    6. 步骤5: 生成答案
-
-    Args:
-        context: 运行上下文
-        max_levels: 最大探索层数,默认4
-
-    返回格式:
-    {
-        "success": True/False,
-        "final_answer": {...},  # 生成的答案
-        "satisfied_notes": [...],  # 满足需求的帖子
-        "message": "..."
-    }
-    """
-
-    # ========== 阶段1:渐进式探索(复用旧流程找到候选query)==========
-
-    # 1.1 提取关键词
-    keyword_result = await extract_keywords(context.q, context)
-    context.keywords = keyword_result.keywords
-
-    # 1.2 渐进式探索各层级
-    current_level = 1
-    candidates_to_evaluate = []
-
-    # Level 1:单个关键词
-    level_1_queries = context.keywords  # 使用所有关键词
-    level_1_data = await explore_level(level_1_queries, current_level, context)
-    analysis_1 = await analyze_level(level_1_data, context.exploration_levels, context.q, context)
-
-    if analysis_1.should_evaluate_now:
-        candidates_to_evaluate.extend(analysis_1.candidates_to_evaluate)
-
-    # Level 2及以后:迭代探索
-    for level_num in range(2, max_levels + 1):
-        prev_analysis: LevelAnalysis = context.level_analyses[-1]["analysis"]
-        prev_analysis = LevelAnalysis(**prev_analysis)
-
-        if not prev_analysis.next_combinations:
-            print(f"\nLevel {level_num-1} 分析后无需继续探索")
-            break
-
-        level_data = await explore_level(prev_analysis.next_combinations, level_num, context)
-        analysis = await analyze_level(level_data, context.exploration_levels, context.q, context)
-
-        if analysis.should_evaluate_now:
-            candidates_to_evaluate.extend(analysis.candidates_to_evaluate)
-
-    if not candidates_to_evaluate:
-        return {
-            "success": False,
-            "final_answer": None,
-            "satisfied_notes": [],
-            "message": "渐进式探索未找到候选query"
-        }
-
-    print(f"\n{'='*60}")
-    print(f"渐进式探索完成,找到 {len(candidates_to_evaluate)} 个候选query")
-    print(f"{'='*60}")
-
-    # ========== 阶段2:新的独立步骤流程 ==========
-
-    # 步骤1: 评估候选query的推荐词
-    evaluation_results = await step_evaluate_query_suggestions(
-        candidates_to_evaluate,
-        context.q,
-        context
-    )
-
-    # 步骤1.5: 筛选合格的推荐词
-    qualified_queries = step_filter_qualified_queries(
-        evaluation_results,
-        context,
-        min_relevance_score=0.7
-    )
-
-    if not qualified_queries:
-        return {
-            "success": False,
-            "final_answer": None,
-            "satisfied_notes": [],
-            "message": "没有合格的推荐词"
-        }
-
-    # 步骤2: 搜索合格的推荐词
-    search_results = await step_search_qualified_queries(
-        qualified_queries,
-        context
-    )
-
-    if not search_results.get('searches'):
-        return {
-            "success": False,
-            "final_answer": None,
-            "satisfied_notes": [],
-            "message": "搜索失败"
-        }
-
-    # 步骤3: 评估搜索到的帖子
-    note_evaluation_data = await step_evaluate_search_notes(
-        search_results,
-        context.q,
-        context
-    )
-
-    # 步骤4: 汇总满足需求的帖子
-    satisfied_notes = step_collect_satisfied_notes(note_evaluation_data)
-
-    if not satisfied_notes:
-        return {
-            "success": False,
-            "final_answer": None,
-            "satisfied_notes": [],
-            "message": "未找到满足需求的帖子"
-        }
-
-    # 步骤5: 生成答案
-    final_answer = await step_generate_answer(
-        satisfied_notes,
-        context.q,
-        context
-    )
-
-    # ========== 返回最终结果 ==========
-
-    return {
-        "success": True,
-        "final_answer": final_answer,
-        "satisfied_notes": satisfied_notes,
-        "message": f"成功找到 {len(satisfied_notes)} 个满足需求的帖子,并生成答案"
-    }
-
-
-# ============================================================================
-# 输出格式化
-# ============================================================================
-
-def format_output(optimization_result: dict, context: RunContext) -> str:
-    """
-    格式化输出结果 - 用于独立步骤流程
-
-    包含:
-    - 生成的答案
-    - 引用的帖子详情
-    - 满足需求的帖子统计
-    """
-    final_answer = optimization_result.get("final_answer")
-    satisfied_notes = optimization_result.get("satisfied_notes", [])
-
-    output = f"原始问题:{context.q}\n"
-    output += f"提取的关键词:{', '.join(context.keywords or [])}\n"
-    output += f"探索层数:{len(context.exploration_levels)}\n"
-    output += f"找到满足需求的帖子:{len(satisfied_notes)} 个\n"
-    output += "\n" + "="*60 + "\n"
-
-    if final_answer:
-        output += "【生成的答案】\n\n"
-        output += final_answer.get("answer", "")
-        output += "\n\n" + "="*60 + "\n"
-
-        output += f"答案置信度:{final_answer.get('confidence', 0):.2f}\n"
-        output += f"答案摘要:{final_answer.get('summary', '')}\n"
-        output += f"引用帖子数:{len(final_answer.get('cited_note_indices', []))} 个\n"
-        output += "\n" + "="*60 + "\n"
-
-        output += "【引用的帖子详情】\n\n"
-        for cited_note in final_answer.get("cited_notes", []):
-            output += f"[{cited_note['index']}] {cited_note['title']}\n"
-            output += f"    置信度: {cited_note['confidence_score']:.2f}\n"
-            output += f"    描述: {cited_note['desc']}\n"
-            output += f"    note_id: {cited_note['note_id']}\n\n"
-    else:
-        output += "未能生成答案\n"
-
-    return output
-
-
-# ============================================================================
-# 主函数
-# ============================================================================
-
-
-
-async def main(input_dir: str, max_levels: int = 4, visualize: bool = False):
-    """
-    主函数 - 使用独立步骤流程(方案A)
-    """
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]
-
-    # 日志保存目录
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-
-    # 执行渐进式探索
-    optimization_result = await progressive_exploration(run_context, max_levels=max_levels)
-
-    # 格式化输出
-    final_output = format_output(optimization_result, run_context)
-    print(f"\n{'='*60}")
-    print("最终结果")
-    print(f"{'='*60}")
-    print(final_output)
-
-    # 保存结果
-    run_context.optimization_result = optimization_result
-    run_context.final_output = final_output
-
-    # 记录最终输出步骤(新格式)
-    final_answer = optimization_result.get("final_answer")
-    satisfied_notes = optimization_result.get("satisfied_notes", [])
-
-    add_step(run_context, "生成最终结果", "final_result", {
-        "success": optimization_result["success"],
-        "message": optimization_result["message"],
-        "satisfied_notes_count": len(satisfied_notes),
-        "final_answer": final_answer,
-        "satisfied_notes_summary": [
-            {
-                "note_id": note["note_id"],
-                "title": note["title"],
-                "confidence_score": note["confidence_score"]
-            }
-            for note in satisfied_notes  # 保存所有满足条件的帖子摘要
-        ] if satisfied_notes else [],
-        "final_output": final_output
-    })
-
-    # 保存 RunContext 到 log_dir(不包含 steps,steps 单独保存)
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    context_dict = run_context.model_dump()
-    context_dict.pop('steps', None)  # 移除 steps,避免数据冗余
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(context_dict, f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-    # 保存步骤化日志
-    steps_file_path = os.path.join(run_context.log_dir, "steps.json")
-    with open(steps_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.steps, f, ensure_ascii=False, indent=2)
-    print(f"Steps log saved to: {steps_file_path}")
-
-    # 如果需要生成可视化
-    if visualize:
-        import subprocess
-        output_html = os.path.join(run_context.log_dir, "visualization.html")
-        print(f"\n🎨 生成可视化HTML...")
-        result = subprocess.run([
-            "python", "sug_v6_1_2_3.visualize.py",
-            steps_file_path,
-            "-o", output_html
-        ])
-        if result.returncode == 0:
-            print(f"✅ 可视化已生成: {output_html}")
-        else:
-            print(f"❌ 可视化生成失败")
-
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具 - v6.1.2.3 独立步骤+答案生成版")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    parser.add_argument(
-        "--max-levels",
-        type=int,
-        default=10,
-        help="最大探索层数,默认: 10"
-    )
-    parser.add_argument(
-        "--visualize",
-        action="store_true",
-        default=True,
-        help="运行完成后自动生成可视化HTML(默认开启)"
-    )
-    parser.add_argument(
-        "--no-visualize",
-        action="store_false",
-        dest="visualize",
-        help="关闭自动生成可视化"
-    )
-    parser.add_argument(
-        "--visualize-only",
-        action="store_true",
-        help="只生成可视化,不运行搜索流程。自动查找input-dir下最新的输出目录"
-    )
-    args = parser.parse_args()
-
-    # 如果只是生成可视化
-    if args.visualize_only:
-        import subprocess
-        import glob
-
-        # 获取版本名称
-        version_name = os.path.splitext(os.path.basename(__file__))[0]
-        output_base = os.path.join(args.input_dir, "output", version_name)
-
-        # 查找最新的输出目录
-        if not os.path.exists(output_base):
-            print(f"❌ 找不到输出目录: {output_base}")
-            sys.exit(1)
-
-        # 获取所有日期目录
-        date_dirs = glob.glob(os.path.join(output_base, "*", "*"))
-        if not date_dirs:
-            print(f"❌ 在 {output_base} 中没有找到输出目录")
-            sys.exit(1)
-
-        # 按修改时间排序,获取最新的
-        latest_dir = max(date_dirs, key=os.path.getmtime)
-        steps_json = os.path.join(latest_dir, "steps.json")
-
-        if not os.path.exists(steps_json):
-            print(f"❌ 找不到 steps.json: {steps_json}")
-            sys.exit(1)
-
-        output_html = os.path.join(latest_dir, "visualization.html")
-        print(f"🎨 找到最新输出目录: {latest_dir}")
-        print(f"🎨 生成可视化: {steps_json} -> {output_html}")
-
-        result = subprocess.run([
-            "python", "sug_v6_1_2_3.visualize.py",
-            steps_json,
-            "-o", output_html
-        ])
-        sys.exit(result.returncode)
-
-    asyncio.run(main(args.input_dir, max_levels=args.max_levels, visualize=args.visualize))

+ 0 - 2180
sug_v6_1_2_3.visualize.py

@@ -1,2180 +0,0 @@
-#!/usr/bin/env python3
-"""
-Steps 可视化工具
-将 steps.json 转换为 HTML 可视化页面
-"""
-
-import json
-import argparse
-from pathlib import Path
-from datetime import datetime
-
-
-HTML_TEMPLATE = """<!DOCTYPE html>
-<html lang="zh-CN">
-<head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>Query Optimization Steps 可视化</title>
-    <style>
-        * {
-            margin: 0;
-            padding: 0;
-            box-sizing: border-box;
-        }
-
-        body {
-            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
-            background: #f5f5f5;
-            color: #333;
-            line-height: 1.6;
-            display: flex;
-            margin: 0;
-            padding: 0;
-        }
-
-        /* 左侧导航 */
-        .sidebar {
-            width: 280px;
-            background: white;
-            height: 100vh;
-            position: fixed;
-            left: 0;
-            top: 0;
-            overflow-y: auto;
-            box-shadow: 2px 0 8px rgba(0,0,0,0.1);
-            z-index: 100;
-        }
-
-        .sidebar-header {
-            padding: 20px;
-            background: #2563eb;
-            color: white;
-            font-size: 18px;
-            font-weight: 600;
-        }
-
-        .toc {
-            padding: 10px 0;
-        }
-
-        .toc-item {
-            padding: 10px 20px;
-            cursor: pointer;
-            transition: background 0.2s;
-            border-left: 3px solid transparent;
-        }
-
-        .toc-item:hover {
-            background: #f0f9ff;
-        }
-
-        .toc-item.active {
-            background: #eff6ff;
-            border-left-color: #2563eb;
-            color: #2563eb;
-            font-weight: 600;
-        }
-
-        .toc-item-level-0 {
-            font-weight: 600;
-            color: #1a1a1a;
-            font-size: 14px;
-        }
-
-        .toc-item-level-1 {
-            padding-left: 35px;
-            font-size: 13px;
-            color: #666;
-        }
-
-        .toc-item-level-2 {
-            padding-left: 50px;
-            font-size: 12px;
-            color: #999;
-        }
-
-        .toc-toggle {
-            display: inline-block;
-            width: 16px;
-            height: 16px;
-            margin-left: 5px;
-            cursor: pointer;
-            transition: transform 0.2s;
-            float: right;
-        }
-
-        .toc-toggle.collapsed {
-            transform: rotate(-90deg);
-        }
-
-        .toc-children {
-            display: none;
-        }
-
-        .toc-children.expanded {
-            display: block;
-        }
-
-        .container {
-            margin-left: 280px;
-            width: calc(100% - 280px);
-            padding: 20px;
-        }
-
-        .header {
-            background: white;
-            padding: 30px;
-            border-radius: 12px;
-            margin-bottom: 30px;
-            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
-        }
-
-        .header h1 {
-            font-size: 32px;
-            margin-bottom: 20px;
-            color: #1a1a1a;
-        }
-
-        .question-box {
-            background: #f0f9ff;
-            padding: 20px;
-            border-radius: 8px;
-            margin-bottom: 20px;
-            border-left: 4px solid #0284c7;
-        }
-
-        .question-label {
-            font-size: 14px;
-            color: #0369a1;
-            margin-bottom: 8px;
-            font-weight: 600;
-        }
-
-        .question-text {
-            font-size: 18px;
-            color: #1a1a1a;
-            line-height: 1.6;
-        }
-
-        .overview {
-            display: flex;
-            gap: 30px;
-            flex-wrap: wrap;
-        }
-
-        .overview-item {
-            flex: 1;
-            min-width: 150px;
-        }
-
-        .overview-label {
-            font-size: 14px;
-            color: #666;
-            margin-bottom: 5px;
-        }
-
-        .overview-value {
-            font-size: 28px;
-            font-weight: bold;
-            color: #2563eb;
-        }
-
-        .step-section {
-            background: white;
-            padding: 30px;
-            border-radius: 12px;
-            margin-bottom: 30px;
-            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
-        }
-
-        .step-header {
-            border-bottom: 3px solid #2563eb;
-            padding-bottom: 15px;
-            margin-bottom: 20px;
-            display: flex;
-            justify-content: space-between;
-            align-items: center;
-        }
-
-        .step-title {
-            font-size: 26px;
-            color: #1a1a1a;
-        }
-
-        .step-type {
-            background: #e0e7ff;
-            color: #4338ca;
-            padding: 6px 15px;
-            border-radius: 20px;
-            font-size: 13px;
-            font-weight: 600;
-            font-family: monospace;
-        }
-
-        .step-content {
-            margin-top: 20px;
-        }
-
-        .info-grid {
-            display: grid;
-            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
-            gap: 20px;
-            margin-bottom: 20px;
-        }
-
-        .info-item {
-            background: #f8f9fa;
-            padding: 15px;
-            border-radius: 8px;
-        }
-
-        .info-label {
-            font-size: 13px;
-            color: #666;
-            margin-bottom: 5px;
-        }
-
-        .info-value {
-            font-size: 20px;
-            font-weight: bold;
-            color: #1a1a1a;
-        }
-
-        .posts-grid {
-            display: grid;
-            grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
-            gap: 20px;
-            margin-top: 20px;
-            padding-top: 100px;
-            margin-top: -80px;
-        }
-
-        .post-card {
-            background: white;
-            border-radius: 8px;
-            overflow: visible;
-            transition: transform 0.2s, box-shadow 0.2s;
-            border: 1px solid #e5e7eb;
-            cursor: pointer;
-            position: relative;
-        }
-
-        .post-card:hover {
-            transform: translateY(-4px);
-            box-shadow: 0 6px 16px rgba(0,0,0,0.15);
-        }
-
-        .post-image-wrapper {
-            width: 100%;
-            background: #f3f4f6;
-            position: relative;
-            padding-top: 133.33%; /* 3:4 aspect ratio */
-            overflow: hidden;
-            border-radius: 8px 8px 0 0;
-        }
-
-        .post-image {
-            position: absolute;
-            top: 0;
-            left: 0;
-            width: 100%;
-            height: 100%;
-            object-fit: cover;
-        }
-
-        .no-image {
-            position: absolute;
-            top: 50%;
-            left: 50%;
-            transform: translate(-50%, -50%);
-            color: #9ca3af;
-            font-size: 14px;
-        }
-
-        .post-type-badge {
-            position: absolute;
-            top: 10px;
-            right: 10px;
-            background: rgba(0, 0, 0, 0.7);
-            color: white;
-            padding: 4px 10px;
-            border-radius: 15px;
-            font-size: 11px;
-            font-weight: 600;
-        }
-
-        .post-info {
-            padding: 15px;
-            position: relative;
-            overflow: visible;
-        }
-
-        .post-title {
-            font-size: 14px;
-            font-weight: 600;
-            margin-bottom: 8px;
-            color: #1a1a1a;
-            display: -webkit-box;
-            -webkit-line-clamp: 2;
-            -webkit-box-orient: vertical;
-            overflow: hidden;
-        }
-
-        .post-desc {
-            font-size: 12px;
-            color: #6b7280;
-            margin-bottom: 10px;
-            display: -webkit-box;
-            -webkit-line-clamp: 2;
-            -webkit-box-orient: vertical;
-            overflow: hidden;
-        }
-
-        .post-meta {
-            display: flex;
-            gap: 15px;
-            margin-bottom: 8px;
-            font-size: 12px;
-            color: #9ca3af;
-        }
-
-        .post-meta-item {
-            display: flex;
-            align-items: center;
-            gap: 4px;
-        }
-
-        .post-author {
-            font-size: 12px;
-            color: #6b7280;
-            margin-bottom: 8px;
-        }
-
-        .post-id {
-            font-size: 10px;
-            color: #9ca3af;
-            font-family: monospace;
-        }
-
-        .evaluation-reason {
-            position: absolute;
-            bottom: calc(100% + 10px);
-            left: 50%;
-            transform: translateX(-50%);
-            background: #2d3748;
-            color: white;
-            padding: 12px 16px;
-            border-radius: 8px;
-            font-size: 12px;
-            line-height: 1.5;
-            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
-            z-index: 1000;
-            display: none;
-            white-space: normal;
-            width: 280px;
-        }
-
-        /* Tooltip 箭头 - 指向下方进度条 */
-        .evaluation-reason::after {
-            content: '';
-            position: absolute;
-            top: 100%;
-            left: 50%;
-            transform: translateX(-50%);
-            border: 6px solid transparent;
-            border-top-color: #2d3748;
-        }
-
-        .confidence-bar:hover .evaluation-reason {
-            display: block !important;
-        }
-
-        /* Debug: 让进度条更明显可悬停 */
-        .confidence-bar {
-            min-height: 32px;
-        }
-
-        .evaluation-reason strong {
-            color: #fbbf24;
-            font-size: 13px;
-        }
-
-        .evaluation-scores {
-            display: flex;
-            gap: 10px;
-            margin-top: 10px;
-            font-size: 12px;
-            flex-wrap: wrap;
-        }
-
-        .score-item {
-            background: rgba(255, 255, 255, 0.15);
-            padding: 5px 10px;
-            border-radius: 12px;
-            color: #fbbf24;
-            border: 1px solid rgba(251, 191, 36, 0.3);
-        }
-
-        .confidence-bar {
-            width: 100%;
-            height: 32px;
-            background: #f3f4f6;
-            position: relative;
-            cursor: help;
-            display: flex;
-            align-items: center;
-            border-radius: 0 0 8px 8px;
-            overflow: hidden;
-        }
-
-        .confidence-bar-fill {
-            height: 100%;
-            transition: width 0.5s ease-out;
-            display: flex;
-            align-items: center;
-            padding: 0 12px;
-            position: relative;
-        }
-
-        .confidence-bar-fill.confidence-low {
-            background: linear-gradient(90deg, #ef4444, #f87171);
-        }
-
-        .confidence-bar-fill.confidence-medium {
-            background: linear-gradient(90deg, #f59e0b, #fbbf24);
-        }
-
-        .confidence-bar-fill.confidence-high {
-            background: linear-gradient(90deg, #10b981, #34d399);
-        }
-
-        .confidence-bar-text {
-            color: white;
-            font-size: 12px;
-            font-weight: 600;
-            white-space: nowrap;
-            position: relative;
-            z-index: 1;
-            text-shadow: 0 1px 2px rgba(0,0,0,0.2);
-        }
-
-        /* 保留旧的badge样式用于兼容 */
-        .confidence-badge {
-            background: #10b981;
-            color: white;
-            padding: 4px 10px;
-            border-radius: 15px;
-            font-size: 12px;
-            font-weight: bold;
-            display: inline-block;
-            margin-bottom: 10px;
-            position: relative;
-            cursor: help;
-        }
-
-        .confidence-low {
-            background: #ef4444;
-        }
-
-        .confidence-medium {
-            background: #f59e0b;
-        }
-
-        .confidence-high {
-            background: #10b981;
-        }
-
-        .query-list {
-            background: #f8f9fa;
-            padding: 20px;
-            border-radius: 8px;
-            margin-top: 15px;
-        }
-
-        .query-item {
-            background: white;
-            padding: 15px;
-            border-radius: 6px;
-            margin-bottom: 10px;
-            border-left: 3px solid #2563eb;
-        }
-
-        .query-text {
-            font-size: 15px;
-            font-weight: 600;
-            color: #1a1a1a;
-            margin-bottom: 5px;
-        }
-
-        .query-meta {
-            font-size: 13px;
-            color: #666;
-        }
-
-        .answer-box {
-            background: #f0fdf4;
-            border: 2px solid #10b981;
-            border-radius: 8px;
-            padding: 25px;
-            margin-top: 20px;
-        }
-
-        .answer-header {
-            font-size: 18px;
-            color: #059669;
-            margin-bottom: 15px;
-            font-weight: 600;
-        }
-
-        .answer-content {
-            font-size: 15px;
-            line-height: 1.8;
-            color: #1a1a1a;
-            white-space: pre-wrap;
-        }
-
-        .answer-meta {
-            margin-top: 15px;
-            padding-top: 15px;
-            border-top: 1px solid #d1fae5;
-            display: flex;
-            gap: 20px;
-            font-size: 13px;
-            color: #059669;
-        }
-
-        .keyword-tags {
-            display: flex;
-            flex-wrap: wrap;
-            gap: 10px;
-            margin-top: 15px;
-        }
-
-        .keyword-tag {
-            background: #dbeafe;
-            color: #1e40af;
-            padding: 6px 12px;
-            border-radius: 15px;
-            font-size: 13px;
-            font-weight: 500;
-        }
-
-        .level-analysis {
-            background: #fef3c7;
-            border-left: 4px solid #f59e0b;
-            padding: 20px;
-            border-radius: 6px;
-            margin-top: 15px;
-        }
-
-        .level-analysis-title {
-            font-size: 16px;
-            color: #92400e;
-            margin-bottom: 10px;
-            font-weight: 600;
-        }
-
-        .level-analysis-text {
-            font-size: 14px;
-            color: #78350f;
-            line-height: 1.8;
-        }
-
-        .timestamp {
-            font-size: 12px;
-            color: #9ca3af;
-            margin-top: 10px;
-        }
-
-        a {
-            color: #2563eb;
-            text-decoration: none;
-        }
-
-        a:hover {
-            text-decoration: underline;
-        }
-
-        /* 模态框样式 */
-        .modal-overlay {
-            display: none;
-            position: fixed;
-            top: 0;
-            left: 0;
-            right: 0;
-            bottom: 0;
-            background: rgba(0, 0, 0, 0.85);
-            z-index: 1000;
-            align-items: center;
-            justify-content: center;
-            padding: 20px;
-            overflow-y: auto;
-        }
-
-        .modal-overlay.active {
-            display: flex;
-        }
-
-        .modal-content {
-            background: white;
-            border-radius: 12px;
-            max-width: 1000px;
-            width: 100%;
-            max-height: 90vh;
-            overflow-y: auto;
-            position: relative;
-            animation: modalSlideIn 0.3s;
-        }
-
-        @keyframes modalSlideIn {
-            from { opacity: 0; transform: translateY(-30px); }
-            to { opacity: 1; transform: translateY(0); }
-        }
-
-        .modal-close {
-            position: sticky;
-            top: 0;
-            right: 0;
-            background: white;
-            border: none;
-            font-size: 36px;
-            color: #6b7280;
-            cursor: pointer;
-            padding: 15px 25px;
-            z-index: 10;
-            text-align: right;
-            border-bottom: 2px solid #e5e7eb;
-            transition: color 0.2s;
-        }
-
-        .modal-close:hover {
-            color: #1f2937;
-        }
-
-        .modal-body {
-            padding: 30px;
-        }
-
-        .modal-title {
-            font-size: 26px;
-            font-weight: 700;
-            color: #1a1a1a;
-            margin-bottom: 15px;
-            line-height: 1.4;
-        }
-
-        .modal-meta {
-            display: flex;
-            gap: 20px;
-            flex-wrap: wrap;
-            margin-bottom: 25px;
-            padding-bottom: 20px;
-            border-bottom: 1px solid #e5e7eb;
-        }
-
-        .modal-meta-item {
-            display: flex;
-            align-items: center;
-            gap: 6px;
-            font-size: 14px;
-            color: #6b7280;
-        }
-
-        .modal-images {
-            margin-bottom: 25px;
-        }
-
-        .modal-images-grid {
-            display: grid;
-            grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
-            gap: 12px;
-        }
-
-        .modal-image-item {
-            border-radius: 8px;
-            overflow: hidden;
-            border: 2px solid #e5e7eb;
-            transition: border-color 0.2s;
-            cursor: pointer;
-        }
-
-        .modal-image-item:hover {
-            border-color: #2563eb;
-        }
-
-        .modal-image {
-            width: 100%;
-            height: auto;
-            display: block;
-            max-height: 250px;
-            object-fit: cover;
-        }
-
-        .modal-section {
-            margin-bottom: 25px;
-        }
-
-        .modal-section-title {
-            font-size: 17px;
-            font-weight: 600;
-            color: #374151;
-            margin-bottom: 12px;
-        }
-
-        .modal-text-content {
-            font-size: 15px;
-            color: #1f2937;
-            line-height: 1.8;
-            white-space: pre-wrap;
-            background: #f9fafb;
-            padding: 18px;
-            border-radius: 8px;
-        }
-
-        .modal-evaluation {
-            background: #fef3c7;
-            border-left: 4px solid #f59e0b;
-            padding: 18px;
-            border-radius: 6px;
-        }
-
-        .modal-link {
-            margin-top: 25px;
-            padding-top: 25px;
-            border-top: 2px solid #e5e7eb;
-            text-align: center;
-        }
-
-        .modal-link-btn {
-            display: inline-flex;
-            align-items: center;
-            gap: 10px;
-            padding: 12px 28px;
-            background: #2563eb;
-            color: white;
-            text-decoration: none;
-            border-radius: 8px;
-            font-size: 15px;
-            font-weight: 600;
-            transition: all 0.2s;
-        }
-
-        .modal-link-btn:hover {
-            background: #1d4ed8;
-            transform: translateY(-2px);
-            box-shadow: 0 4px 12px rgba(37, 99, 235, 0.3);
-        }
-
-        /* 卡片上的图片轮播指示器 */
-        .carousel-arrow {
-            position: absolute;
-            top: 50%;
-            transform: translateY(-50%);
-            background: rgba(0, 0, 0, 0.6);
-            color: white;
-            border: none;
-            width: 36px;
-            height: 36px;
-            border-radius: 50%;
-            font-size: 20px;
-            cursor: pointer;
-            z-index: 15;
-            display: flex;
-            align-items: center;
-            justify-content: center;
-            transition: all 0.2s;
-            opacity: 0;
-        }
-
-        .post-image-wrapper:hover .carousel-arrow {
-            opacity: 1;
-        }
-
-        .carousel-arrow:hover {
-            background: rgba(0, 0, 0, 0.8);
-            transform: translateY(-50%) scale(1.1);
-        }
-
-        .carousel-arrow.left {
-            left: 8px;
-        }
-
-        .carousel-arrow.right {
-            right: 8px;
-        }
-
-        /* 可折叠区域样式 */
-        .collapsible-section {
-            margin: 20px 0;
-        }
-
-        .collapsible-header {
-            background: #f3f4f6;
-            padding: 12px 15px;
-            border-radius: 8px;
-            cursor: pointer;
-            display: flex;
-            align-items: center;
-            gap: 10px;
-            transition: background 0.2s;
-            user-select: none;
-        }
-
-        .collapsible-header:hover {
-            background: #e5e7eb;
-        }
-
-        .collapsible-toggle {
-            font-size: 14px;
-            transition: transform 0.2s;
-        }
-
-        .collapsible-toggle.collapsed {
-            transform: rotate(-90deg);
-        }
-
-        .collapsible-title {
-            font-weight: 600;
-            font-size: 16px;
-            color: #374151;
-        }
-
-        .collapsible-content {
-            max-height: 10000px;
-            overflow: hidden;
-            transition: max-height 0.3s ease-out, opacity 0.3s ease-out;
-            opacity: 1;
-        }
-
-        .collapsible-content.collapsed {
-            max-height: 0;
-            opacity: 0;
-        }
-    </style>
-</head>
-<body>
-    <!-- 左侧导航 -->
-    <div class="sidebar">
-        <div class="sidebar-header">📑 目录</div>
-        <div class="toc" id="toc"></div>
-    </div>
-
-    <!-- 主内容区 -->
-    <div class="container">
-        {content}
-    </div>
-
-    <!-- 模态框 -->
-    <div id="postModal" class="modal-overlay" onclick="if(event.target === this) closeModal()">
-        <div class="modal-content" onclick="event.stopPropagation()">
-            <button class="modal-close" onclick="closeModal()">&times;</button>
-            <div class="modal-body" id="modalBody">
-                <!-- 动态内容 -->
-            </div>
-        </div>
-    </div>
-
-    <script>
-        // 模态框功能
-        function openModal(postData) {
-            const modal = document.getElementById('postModal');
-            const modalBody = document.getElementById('modalBody');
-
-            // 构建图片网格
-            let imagesHtml = '';
-            if (postData.images && postData.images.length > 0) {
-                imagesHtml = '<div class="modal-images"><div class="modal-images-grid">';
-                postData.images.forEach((img, idx) => {
-                    imagesHtml += `<div class="modal-image-item"><img src="${img}" class="modal-image" alt="图片 ${idx + 1}"></div>`;
-                });
-                imagesHtml += '</div></div>';
-            }
-
-            // 构建评估详情
-            let evalHtml = '';
-            if (postData.evaluation) {
-                evalHtml = `
-                <div class="modal-section">
-                    <div class="modal-section-title">💡 评估详情</div>
-                    <div class="modal-evaluation">
-                        <div style="margin-bottom: 12px;"><strong>评估理由:</strong></div>
-                        <div style="color: #78350f; line-height: 1.8;">${postData.evaluation.reason || '无'}</div>
-                        <div class="evaluation-scores" style="margin-top: 12px;">
-                            <span class="score-item">📌 标题相关性: ${postData.evaluation.title_relevance?.toFixed(2) || '0.00'}</span>
-                            <span class="score-item">📄 内容期望: ${postData.evaluation.content_expectation?.toFixed(2) || '0.00'}</span>
-                            <span class="score-item">🎯 置信度: ${postData.evaluation.confidence_score?.toFixed(2) || '0.00'}</span>
-                        </div>
-                    </div>
-                </div>`;
-            }
-
-            modalBody.innerHTML = `
-                <div class="modal-title">${postData.title}</div>
-                <div class="modal-meta">
-                    <div class="modal-meta-item">👤 ${postData.user}</div>
-                    <div class="modal-meta-item">❤️ ${postData.likes}</div>
-                    <div class="modal-meta-item">⭐ ${postData.collects}</div>
-                    <div class="modal-meta-item">💬 ${postData.comments}</div>
-                    ${postData.type === 'video' ? '<div class="modal-meta-item">📹 视频</div>' : ''}
-                </div>
-                ${imagesHtml}
-                <div class="modal-section">
-                    <div class="modal-section-title">📝 描述</div>
-                    <div class="modal-text-content">${postData.desc || '无描述'}</div>
-                </div>
-                ${evalHtml}
-                <div class="modal-link">
-                    <a href="${postData.url}" target="_blank" class="modal-link-btn">
-                        🔗 在小红书中查看
-                    </a>
-                </div>
-            `;
-
-            modal.classList.add('active');
-            document.body.style.overflow = 'hidden';
-        }
-
-        function closeModal() {
-            const modal = document.getElementById('postModal');
-            modal.classList.remove('active');
-            document.body.style.overflow = '';
-        }
-
-        // ESC键关闭模态框
-        document.addEventListener('keydown', function(e) {
-            if (e.key === 'Escape') {
-                closeModal();
-            }
-        });
-
-        // 卡片上的图片轮播(使用左右箭头按钮)
-        function initCarousels() {
-            document.querySelectorAll('.post-card').forEach(card => {
-                const images = JSON.parse(card.dataset.images || '[]');
-                if (images.length <= 1) return;
-
-                let currentIndex = 0;
-                const imgElement = card.querySelector('.post-image');
-                const leftArrow = card.querySelector('.carousel-arrow.left');
-                const rightArrow = card.querySelector('.carousel-arrow.right');
-
-                // 左箭头点击
-                if (leftArrow) {
-                    leftArrow.addEventListener('click', function(e) {
-                        e.stopPropagation();
-                        currentIndex = (currentIndex - 1 + images.length) % images.length;
-                        if (imgElement) {
-                            imgElement.src = images[currentIndex];
-                        }
-                    });
-                }
-
-                // 右箭头点击
-                if (rightArrow) {
-                    rightArrow.addEventListener('click', function(e) {
-                        e.stopPropagation();
-                        currentIndex = (currentIndex + 1) % images.length;
-                        if (imgElement) {
-                            imgElement.src = images[currentIndex];
-                        }
-                    });
-                }
-            });
-        }
-
-        // 生成目录(显示步骤和可折叠的子项)
-        function generateTOC() {
-            const toc = document.getElementById('toc');
-            const sections = document.querySelectorAll('.step-section');
-
-            sections.forEach((section, index) => {
-                const title = section.querySelector('.step-title')?.textContent || `步骤 ${index + 1}`;
-                const id = `step-${index}`;
-                section.id = id;
-
-                // 查找该section下的直接子可折叠项(不包括嵌套的)
-                const collapsibleSections = section.querySelectorAll(':scope > .step-content > .collapsible-section[id]');
-
-                // 创建步骤项
-                const stepItem = document.createElement('div');
-                stepItem.className = 'toc-item toc-item-level-0';
-
-                if (collapsibleSections.length > 0) {
-                    // 如果有子项,添加展开/折叠图标(箭头放在右侧)
-                    const toggleId = `toc-toggle-${index}`;
-                    stepItem.innerHTML = `<span>${title}</span><span class="toc-toggle" id="${toggleId}">▼</span>`;
-
-                    const toggle = stepItem.querySelector('.toc-toggle');
-                    const childrenId = `toc-children-${index}`;
-                    toggle.onclick = (e) => {
-                        e.stopPropagation();
-                        toggle.classList.toggle('collapsed');
-                        const children = document.getElementById(childrenId);
-                        if (children) {
-                            children.classList.toggle('expanded');
-                        }
-                    };
-                } else {
-                    stepItem.textContent = title;
-                }
-
-                stepItem.onclick = (e) => {
-                    if (!e.target.classList.contains('toc-toggle')) {
-                        scrollToSection(id);
-                    }
-                };
-
-                toc.appendChild(stepItem);
-
-                // 添加子项目录(支持嵌套)
-                if (collapsibleSections.length > 0) {
-                    const childrenContainer = document.createElement('div');
-                    childrenContainer.id = `toc-children-${index}`;
-                    childrenContainer.className = 'toc-children expanded';
-
-                    collapsibleSections.forEach(collapsible => {
-                        const subTitle = collapsible.getAttribute('data-title') || '子项';
-                        const subId = collapsible.id;
-
-                        const subItem = document.createElement('div');
-                        subItem.className = 'toc-item toc-item-level-1';
-                        subItem.textContent = subTitle;
-                        subItem.onclick = () => scrollToSection(subId);
-
-                        childrenContainer.appendChild(subItem);
-
-                        // 查找该可折叠区域内的嵌套可折叠区域
-                        const nestedCollapsibles = collapsible.querySelectorAll(':scope > .collapsible-content > .collapsible-section[id]');
-                        if (nestedCollapsibles.length > 0) {
-                            nestedCollapsibles.forEach(nested => {
-                                const nestedTitle = nested.getAttribute('data-title') || '子项';
-                                const nestedId = nested.id;
-
-                                const nestedItem = document.createElement('div');
-                                nestedItem.className = 'toc-item toc-item-level-2';
-                                nestedItem.textContent = nestedTitle;
-                                nestedItem.onclick = () => scrollToSection(nestedId);
-
-                                childrenContainer.appendChild(nestedItem);
-                            });
-                        }
-                    });
-
-                    toc.appendChild(childrenContainer);
-                }
-            });
-        }
-
-        // 滚动到指定section
-        function scrollToSection(id) {
-            const element = document.getElementById(id);
-            if (element) {
-                const offset = 80;
-                const elementPosition = element.getBoundingClientRect().top;
-                const offsetPosition = elementPosition + window.pageYOffset - offset;
-
-                window.scrollTo({
-                    top: offsetPosition,
-                    behavior: 'smooth'
-                });
-
-                // 更新active状态
-                document.querySelectorAll('.toc-item').forEach(item => item.classList.remove('active'));
-                event.target.classList.add('active');
-            }
-        }
-
-        // 滚动时高亮当前section
-        function updateActiveTOC() {
-            const sections = document.querySelectorAll('.step-section');
-            const tocItems = document.querySelectorAll('.toc-item');
-
-            let currentIndex = -1;
-            sections.forEach((section, index) => {
-                const rect = section.getBoundingClientRect();
-                if (rect.top <= 100) {
-                    currentIndex = index;
-                }
-            });
-
-            tocItems.forEach((item, index) => {
-                item.classList.toggle('active', index === currentIndex);
-            });
-        }
-
-        // 初始化可折叠区域
-        function initCollapsibles() {
-            document.querySelectorAll('.collapsible-header').forEach(header => {
-                header.addEventListener('click', function() {
-                    const toggle = this.querySelector('.collapsible-toggle');
-                    const content = this.nextElementSibling;
-
-                    if (content && content.classList.contains('collapsible-content')) {
-                        toggle.classList.toggle('collapsed');
-                        content.classList.toggle('collapsed');
-                    }
-                });
-            });
-        }
-
-        // 页面加载完成后初始化
-        document.addEventListener('DOMContentLoaded', function() {
-            initCarousels();
-            generateTOC();
-            initCollapsibles();
-            window.addEventListener('scroll', updateActiveTOC);
-            updateActiveTOC();
-        });
-    </script>
-</body>
-</html>
-"""
-
-
-def make_collapsible(title, content, collapsed=True, section_id=None):
-    """创建可折叠区域的HTML"""
-    collapsed_class = " collapsed" if collapsed else ""
-    id_attr = f' id="{section_id}"' if section_id else ""
-    # 添加 data-title 属性用于目录生成
-    title_attr = f' data-title="{title}"' if section_id else ""
-    return f"""
-    <div class="collapsible-section"{id_attr}{title_attr}>
-        <div class="collapsible-header">
-            <span class="collapsible-toggle{collapsed_class}">▼</span>
-            <span class="collapsible-title">{title}</span>
-        </div>
-        <div class="collapsible-content{collapsed_class}">
-            {content}
-        </div>
-    </div>
-    """
-
-
-def get_confidence_class(score):
-    """根据置信度分数返回CSS类"""
-    if score >= 0.7:
-        return "confidence-high"
-    elif score >= 0.5:
-        return "confidence-medium"
-    else:
-        return "confidence-low"
-
-
-def escape_js_string(s):
-    """转义JavaScript字符串"""
-    import json
-    return json.dumps(str(s) if s else "")
-
-
-def build_post_json_data(note, evaluation=None):
-    """构建帖子的JSON数据用于模态框"""
-    import json
-
-    image_list = note.get('image_list', [])
-    if not image_list and note.get('cover_image'):
-        cover = note.get('cover_image')
-        # cover_image 可能是字典或字符串
-        if isinstance(cover, dict):
-            image_list = [cover.get('image_url', '')]
-        else:
-            image_list = [cover]
-
-    # image_list 现在已经是 URL 字符串列表(由搜索API预处理)
-    images = [img if isinstance(img, str) else img.get('image_url', '') for img in image_list if img]
-
-    interact = note.get('interact_info', {})
-    user = note.get('user', {})
-
-    data = {
-        'title': note.get('title', '无标题'),
-        'desc': note.get('desc', ''),
-        'user': user.get('nickname', '未知'),
-        'likes': interact.get('liked_count', 0),
-        'collects': interact.get('collected_count', 0),
-        'comments': interact.get('comment_count', 0),
-        'type': note.get('type', 'normal'),
-        'url': note.get('note_url', ''),
-        'images': images
-    }
-
-    if evaluation:
-        data['evaluation'] = {
-            'reason': evaluation.get('reason', ''),
-            'title_relevance': evaluation.get('title_relevance', 0),
-            'content_expectation': evaluation.get('content_expectation', 0),
-            'confidence_score': evaluation.get('confidence_score', 0)
-        }
-
-    return json.dumps(data, ensure_ascii=False)
-
-
-def render_header(steps_data):
-    """渲染页面头部"""
-    # 获取基本信息
-    first_step = steps_data[0] if steps_data else {}
-    last_step = steps_data[-1] if steps_data else {}
-
-    original_question = ""
-    keywords = []
-    total_steps = len(steps_data)
-    satisfied_notes = 0
-
-    # 提取关键信息
-    for step in steps_data:
-        if step.get("step_type") == "keyword_extraction":
-            original_question = step.get("data", {}).get("input_question", "")
-            keywords = step.get("data", {}).get("keywords", [])
-        elif step.get("step_type") == "final_result":
-            satisfied_notes = step.get("data", {}).get("satisfied_notes_count", 0)
-
-    keywords_html = "".join([f'<span class="keyword-tag">{k}</span>' for k in keywords])
-
-    html = f"""
-    <div class="header">
-        <h1>🔍 Query Optimization Steps</h1>
-        <div class="question-box">
-            <div class="question-label">原始问题</div>
-            <div class="question-text">{original_question}</div>
-        </div>
-        {f'<div class="keyword-tags">{keywords_html}</div>' if keywords else ''}
-        <div class="overview">
-            <div class="overview-item">
-                <div class="overview-label">总步骤数</div>
-                <div class="overview-value">{total_steps}</div>
-            </div>
-            <div class="overview-item">
-                <div class="overview-label">满足需求的帖子</div>
-                <div class="overview-value">{satisfied_notes}</div>
-            </div>
-        </div>
-    </div>
-    """
-    return html
-
-
-def render_keyword_extraction(step):
-    """渲染关键词提取步骤"""
-    data = step.get("data", {})
-    keywords = data.get("keywords", [])
-    reasoning = data.get("reasoning", "")
-
-    keywords_html = "".join([f'<span class="keyword-tag">{k}</span>' for k in keywords])
-
-    html = f"""
-    <div class="step-section">
-        <div class="step-header">
-            <div class="step-title">步骤 {step['step_number']}: {step['step_name']}</div>
-            <div class="step-type">{step['step_type']}</div>
-        </div>
-        <div class="step-content">
-            <div class="keyword-tags">{keywords_html}</div>
-            {f'<p style="margin-top: 15px; color: #666; font-size: 14px;">{reasoning}</p>' if reasoning else ''}
-        </div>
-        <div class="timestamp">⏰ {step.get('timestamp', '')}</div>
-    </div>
-    """
-    return html
-
-
-def render_level_exploration(step):
-    """渲染层级探索步骤"""
-    data = step.get("data", {})
-    level = data.get("level", 0)
-    query_count = data.get("query_count", 0)
-    results = data.get("results", [])
-
-    queries_html = ""
-    for result in results:
-        query = result.get("query", "")
-        suggestions = result.get("suggestions", [])
-
-        # 使用标签样式显示推荐词
-        suggestions_tags = ""
-        for suggestion in suggestions:
-            suggestions_tags += f'<span class="keyword-tag" style="margin: 3px;">{suggestion}</span>'
-
-        queries_html += f"""
-        <div class="query-item">
-            <div class="query-text">{query}</div>
-            <div style="margin-top: 10px;">
-                <div style="color: #666; font-size: 13px; margin-bottom: 5px;">推荐词 ({len(suggestions)} 个):</div>
-                <div style="display: flex; flex-wrap: wrap; gap: 5px;">
-                    {suggestions_tags}
-                </div>
-            </div>
-        </div>
-        """
-
-    html = f"""
-    <div class="step-section">
-        <div class="step-header">
-            <div class="step-title">步骤 {step['step_number']}: Level {level} 探索</div>
-            <div class="step-type">{step['step_type']}</div>
-        </div>
-        <div class="step-content">
-            <div class="info-grid">
-                <div class="info-item">
-                    <div class="info-label">探索query数</div>
-                    <div class="info-value">{query_count}</div>
-                </div>
-                <div class="info-item">
-                    <div class="info-label">获得推荐词总数</div>
-                    <div class="info-value">{data.get('total_suggestions', 0)}</div>
-                </div>
-            </div>
-            <div class="query-list">{queries_html}</div>
-        </div>
-        <div class="timestamp">⏰ {step.get('timestamp', '')}</div>
-    </div>
-    """
-    return html
-
-
-def render_level_analysis(step):
-    """渲染层级分析步骤"""
-    data = step.get("data", {})
-    level = data.get("level", 0)
-    key_findings = data.get("key_findings", "")
-    should_evaluate = data.get("should_evaluate_now", False)
-    promising_signals_count = data.get("promising_signals_count", 0)
-    next_combinations = data.get("next_combinations", [])
-    promising_signals = data.get("promising_signals", [])
-    reasoning = data.get("reasoning", "")
-    step_num = step['step_number']
-
-    # 渲染推理过程
-    reasoning_html = ""
-    if reasoning:
-        reasoning_html = f"""
-        <div style="margin-top: 20px;">
-            <div class="level-analysis">
-                <div class="level-analysis-title">💭 推理过程</div>
-                <div class="level-analysis-text">{reasoning}</div>
-            </div>
-        </div>
-        """
-
-    # 渲染下一层探索
-    next_html = ""
-    if next_combinations:
-        next_items = "".join([f'<span class="keyword-tag">{q}</span>' for q in next_combinations])
-        next_html = f'<div style="margin-top: 15px;"><strong>下一层探索:</strong><div class="keyword-tags" style="margin-top: 10px;">{next_items}</div></div>'
-
-    # 渲染有价值的信号
-    signals_html = ""
-    if promising_signals:
-        signals_items = ""
-        for signal in promising_signals:
-            query = signal.get("query", "")
-            from_level = signal.get("from_level", "")
-            reason = signal.get("reason", "")
-
-            signals_items += f"""
-            <div class="query-item" style="border-left: 3px solid #10b981; padding-left: 15px;">
-                <div class="query-text" style="font-weight: 600;">{query}</div>
-                <div style="margin-top: 8px; color: #666; font-size: 13px;">
-                    <span style="color: #10b981;">来自 Level {from_level}</span>
-                </div>
-                <div style="margin-top: 8px; color: #555; font-size: 14px; line-height: 1.5;">
-                    {reason}
-                </div>
-            </div>
-            """
-
-        signals_html = make_collapsible(
-            f"💡 有价值的信号 ({len(promising_signals)} 个)",
-            f'<div style="display: flex; flex-direction: column; gap: 15px; margin-top: 10px;">{signals_items}</div>',
-            collapsed=True,
-            section_id=f"step{step_num}-signals"
-        )
-
-    html = f"""
-    <div class="step-section">
-        <div class="step-header">
-            <div class="step-title">步骤 {step['step_number']}: Level {level} 分析</div>
-            <div class="step-type">{step['step_type']}</div>
-        </div>
-        <div class="step-content">
-            <div class="level-analysis">
-                <div class="level-analysis-title">🔎 关键发现</div>
-                <div class="level-analysis-text">{key_findings}</div>
-            </div>
-            <div class="info-grid" style="margin-top: 20px;">
-                <div class="info-item">
-                    <div class="info-label">有价值信号数</div>
-                    <div class="info-value">{promising_signals_count}</div>
-                </div>
-                <div class="info-item">
-                    <div class="info-label">是否开始评估</div>
-                    <div class="info-value">{'是' if should_evaluate else '否'}</div>
-                </div>
-            </div>
-            {signals_html}
-            {reasoning_html}
-            {next_html}
-        </div>
-        <div class="timestamp">⏰ {step.get('timestamp', '')}</div>
-    </div>
-    """
-    return html
-
-
-def render_search_results(step):
-    """渲染搜索结果步骤"""
-    data = step.get("data", {})
-    search_results = data.get("search_results", [])
-
-    posts_html = ""
-    step_num = step['step_number']
-    for idx, sr in enumerate(search_results):
-        query = sr.get("query", "")
-        note_count = sr.get("note_count", 0)
-        notes_summary = sr.get("notes_summary", [])
-
-        # 渲染该query的帖子
-        posts_cards = ""
-        for note in notes_summary:
-            # 获取封面图
-            image_list = note.get('image_list', [])
-            if image_list:
-                # image_list 已经是 URL 字符串列表,第一张就是封面
-                cover_url = image_list[0] if isinstance(image_list[0], str) else image_list[0].get('image_url', '')
-            else:
-                cover = note.get("cover_image", {})
-                cover_url = cover.get("image_url", "") if isinstance(cover, dict) else cover if cover else ""
-
-            interact = note.get("interact_info", {})
-            user = note.get("user", {})
-
-            # image_list 现在已经是 URL 字符串列表
-            images = [img if isinstance(img, str) else img.get('image_url', '') for img in image_list if img]
-
-            # 构建帖子数据用于模态框
-            post_data = build_post_json_data(note)
-            images_json = json.dumps(images)
-
-            image_html = f'<img src="{cover_url}" class="post-image" alt="{note.get("title", "")}">' if cover_url else '<div class="no-image">无图片</div>'
-
-            type_badge = ""
-            if note.get("type") == "video":
-                type_badge = '<div class="post-type-badge">📹 视频</div>'
-
-            # 轮播箭头按钮
-            arrows_html = ""
-            if len(images) > 1:
-                arrows_html = '''
-                    <button class="carousel-arrow left" onclick="event.stopPropagation()">‹</button>
-                    <button class="carousel-arrow right" onclick="event.stopPropagation()">›</button>
-                '''
-
-            posts_cards += f"""
-            <div class="post-card" onclick='openModal({post_data})' data-images='{images_json}'>
-                <div class="post-image-wrapper">
-                    {image_html}
-                    {type_badge}
-                    {arrows_html}
-                </div>
-                <div class="post-info">
-                    <div class="post-title">{note.get('title', '无标题')}</div>
-                    <div class="post-desc">{note.get('desc', '')}</div>
-                    <div class="post-meta">
-                        <div class="post-meta-item">❤️ {interact.get('liked_count', 0)}</div>
-                        <div class="post-meta-item">⭐ {interact.get('collected_count', 0)}</div>
-                        <div class="post-meta-item">💬 {interact.get('comment_count', 0)}</div>
-                    </div>
-                    <div class="post-author">👤 {user.get('nickname', '未知')}</div>
-                    <div class="post-id">{note.get('note_id', '')}</div>
-                </div>
-            </div>
-            """
-
-        # 使用可折叠区域包装每个query的搜索结果,添加唯一ID
-        query_content = f'<div class="posts-grid">{posts_cards}</div>'
-        posts_html += make_collapsible(
-            f"🔎 {query} (找到 {note_count} 个帖子)",
-            query_content,
-            collapsed=True,
-            section_id=f"step{step_num}-search-{idx}"
-        )
-
-    html = f"""
-    <div class="step-section">
-        <div class="step-header">
-            <div class="step-title">步骤 {step['step_number']}: 搜索结果</div>
-            <div class="step-type">{step['step_type']}</div>
-        </div>
-        <div class="step-content">
-            <div class="info-grid">
-                <div class="info-item">
-                    <div class="info-label">搜索query数</div>
-                    <div class="info-value">{data.get('qualified_count', 0)}</div>
-                </div>
-            </div>
-            {posts_html}
-        </div>
-        <div class="timestamp">⏰ {step.get('timestamp', '')}</div>
-    </div>
-    """
-    return html
-
-
-def render_note_evaluations(step):
-    """渲染帖子评估步骤"""
-    data = step.get("data", {})
-    note_evaluations = data.get("note_evaluations", [])
-    total_satisfied = data.get("total_satisfied", 0)
-
-    evals_html = ""
-    step_num = step["step_number"]
-    for idx, query_eval in enumerate(note_evaluations):
-        query = query_eval.get("query", "")
-        satisfied_count = query_eval.get("satisfied_count", 0)
-        evaluated_notes = query_eval.get("evaluated_notes", [])
-
-        # 分离满足和不满足需求的帖子
-        satisfied_notes = [n for n in evaluated_notes if n.get('evaluation', {}).get('need_satisfaction')]
-        unsatisfied_notes = [n for n in evaluated_notes if not n.get('evaluation', {}).get('need_satisfaction')]
-
-        # 渲染满足需求的帖子
-        satisfied_cards = ""
-        for note in satisfied_notes:
-            # 获取封面图
-            image_list = note.get('image_list', [])
-            if image_list:
-                cover_url = image_list[0] if isinstance(image_list[0], str) else image_list[0].get('image_url', '')
-            else:
-                cover = note.get("cover_image", {})
-                cover_url = cover.get("image_url", "") if isinstance(cover, dict) else cover if cover else ""
-
-            interact = note.get("interact_info", {})
-            user = note.get("user", {})
-            evaluation = note.get("evaluation", {})
-            confidence = evaluation.get("confidence_score", 0)
-
-            # image_list 现在已经是 URL 字符串列表
-            images = [img if isinstance(img, str) else img.get('image_url', '') for img in image_list if img]
-
-            # 构建帖子数据用于模态框
-            post_data = build_post_json_data(note, evaluation)
-            images_json = json.dumps(images)
-
-            image_html = f'<img src="{cover_url}" class="post-image" alt="{note.get("title", "")}">' if cover_url else '<div class="no-image">无图片</div>'
-
-            type_badge = ""
-            if note.get("type") == "video":
-                type_badge = '<div class="post-type-badge">📹 视频</div>'
-
-            # 轮播箭头按钮
-            arrows_html = ""
-            if len(images) > 1:
-                arrows_html = '''
-                    <button class="carousel-arrow left" onclick="event.stopPropagation()">‹</button>
-                    <button class="carousel-arrow right" onclick="event.stopPropagation()">›</button>
-                '''
-
-            # 评估详情
-            eval_reason = evaluation.get("reason", "")
-            title_rel = evaluation.get("title_relevance", 0)
-            content_exp = evaluation.get("content_expectation", 0)
-
-            eval_details = ""
-
-            # 置信度百分比
-            confidence_percent = int(confidence * 100)
-
-            satisfied_cards += f"""
-            <div class="post-card" onclick='openModal({post_data})' data-images='{images_json}'>
-                <div class="post-image-wrapper">
-                    {image_html}
-                    {type_badge}
-                    {arrows_html}
-                </div>
-                <div class="post-info">
-                    <div class="post-title">{note.get('title', '无标题')}</div>
-                    <div class="post-desc">{note.get('desc', '')}</div>
-                    <div class="post-meta">
-                        <div class="post-meta-item">❤️ {interact.get('liked_count', 0)}</div>
-                        <div class="post-meta-item">⭐ {interact.get('collected_count', 0)}</div>
-                        <div class="post-meta-item">💬 {interact.get('comment_count', 0)}</div>
-                    </div>
-                    <div class="post-author">👤 {user.get('nickname', '未知')}</div>
-                    <div class="post-id">{note.get('note_id', '')}</div>
-                </div>
-                <div class="confidence-bar">
-                    <div class="confidence-bar-fill {get_confidence_class(confidence)}" style="width: {confidence_percent}%">
-                        <span class="confidence-bar-text">置信度: {confidence:.2f}</span>
-                    </div>
-                    {eval_details}
-                </div>
-            </div>
-            """
-
-        # 渲染不满足需求的帖子
-        unsatisfied_cards = ""
-        for note in unsatisfied_notes:
-            # 获取封面图
-            image_list = note.get('image_list', [])
-            if image_list:
-                cover_url = image_list[0] if isinstance(image_list[0], str) else image_list[0].get('image_url', '')
-            else:
-                cover = note.get("cover_image", {})
-                cover_url = cover.get("image_url", "") if isinstance(cover, dict) else cover if cover else ""
-
-            interact = note.get("interact_info", {})
-            user = note.get("user", {})
-            evaluation = note.get("evaluation", {})
-            confidence = evaluation.get("confidence_score", 0)
-
-            # image_list 现在已经是 URL 字符串列表
-            images = [img if isinstance(img, str) else img.get('image_url', '') for img in image_list if img]
-
-            post_data = build_post_json_data(note, evaluation)
-            images_json = json.dumps(images)
-
-            image_html = f'<img src="{cover_url}" class="post-image" alt="{note.get("title", "")}">' if cover_url else '<div class="no-image">无图片</div>'
-
-            type_badge = ""
-            if note.get("type") == "video":
-                type_badge = '<div class="post-type-badge">📹 视频</div>'
-
-            arrows_html = ""
-            if len(images) > 1:
-                arrows_html = '''
-        <button class="carousel-arrow left" onclick="event.stopPropagation()">‹</button>
-        <button class="carousel-arrow right" onclick="event.stopPropagation()">›</button>
-    '''
-
-            eval_reason = evaluation.get("reason", "")
-            title_rel = evaluation.get("title_relevance", 0)
-            content_exp = evaluation.get("content_expectation", 0)
-
-            eval_details = ""
-
-            confidence_percent = int(confidence * 100)
-
-            unsatisfied_cards += f"""
-            <div class="post-card" onclick='openModal({post_data})' data-images='{images_json}'>
-                <div class="post-image-wrapper">
-                    {image_html}
-                    {type_badge}
-                    {arrows_html}
-                </div>
-                <div class="post-info">
-                    <div class="post-title">{note.get('title', '无标题')}</div>
-                    <div class="post-desc">{note.get('desc', '')}</div>
-                    <div class="post-meta">
-                        <div class="post-meta-item">❤️ {interact.get('liked_count', 0)}</div>
-                        <div class="post-meta-item">⭐ {interact.get('collected_count', 0)}</div>
-                        <div class="post-meta-item">💬 {interact.get('comment_count', 0)}</div>
-                    </div>
-                    <div class="post-author">👤 {user.get('nickname', '未知')}</div>
-                    <div class="post-id">{note.get('note_id', '')}</div>
-                </div>
-                <div class="confidence-bar">
-                    <div class="confidence-bar-fill {get_confidence_class(confidence)}" style="width: {confidence_percent}%">
-                        <span class="confidence-bar-text">置信度: {confidence:.2f}</span>
-                    </div>
-                    {eval_details}
-                </div>
-            </div>
-            """
-
-        # 构建该query的评估结果,使用嵌套可折叠区域
-        query_sections = ""
-        if satisfied_cards:
-            query_sections += make_collapsible(
-                f"✅ 满足需求 ({len(satisfied_notes)} 个帖子)",
-                f'<div class="posts-grid">{satisfied_cards}</div>',
-                collapsed=True,
-                section_id=f"step{step_num}-eval-{idx}-satisfied"
-            )
-        if unsatisfied_cards:
-            query_sections += make_collapsible(
-                f"❌ 不满足需求 ({len(unsatisfied_notes)} 个帖子)",
-                f'<div class="posts-grid">{unsatisfied_cards}</div>',
-                collapsed=True,
-                section_id=f"step{step_num}-eval-{idx}-unsatisfied"
-            )
-
-        if query_sections:
-            # 使用可折叠区域包装每个query的评估结果
-            evals_html += make_collapsible(
-                f"📊 {query} ({satisfied_count}/{len(evaluated_notes)} 个满足需求)",
-                query_sections,
-                collapsed=True,
-                section_id=f"step{step_num}-eval-{idx}"
-            )
-
-    html = f"""
-    <div class="step-section">
-        <div class="step-header">
-            <div class="step-title">步骤 {step['step_number']}: 帖子评估结果</div>
-            <div class="step-type">{step['step_type']}</div>
-        </div>
-        <div class="step-content">
-            <div class="info-grid">
-                <div class="info-item">
-                    <div class="info-label">评估的query数</div>
-                    <div class="info-value">{data.get('query_count', 0)}</div>
-                </div>
-                <div class="info-item">
-                    <div class="info-label">总帖子数</div>
-                    <div class="info-value">{data.get('total_notes', 0)}</div>
-                </div>
-                <div class="info-item">
-                    <div class="info-label">满足需求的帖子</div>
-                    <div class="info-value">{total_satisfied}</div>
-                </div>
-            </div>
-            {evals_html}
-        </div>
-        <div class="timestamp">⏰ {step.get('timestamp', '')}</div>
-    </div>
-    """
-    return html
-
-
-def render_answer_generation(step):
-    """渲染答案生成步骤"""
-    data = step.get("data", {})
-    result = data.get("result", {})
-    answer = result.get("answer", "")
-    confidence = result.get("confidence", 0)
-    summary = result.get("summary", "")
-    cited_notes = result.get("cited_notes", [])
-
-    # 渲染引用的帖子
-    cited_html = ""
-    for note in cited_notes:
-        # 获取封面图
-        image_list = note.get('image_list', [])
-        if image_list:
-            cover_url = image_list[0] if isinstance(image_list[0], str) else image_list[0].get('image_url', '')
-        else:
-            cover = note.get("cover_image", {})
-            cover_url = cover.get("image_url", "") if isinstance(cover, dict) else cover if cover else ""
-
-        interact = note.get("interact_info", {})
-        user = note.get("user", {})
-
-        # image_list 现在已经是 URL 字符串列表
-        images = [img if isinstance(img, str) else img.get('image_url', '') for img in image_list if img]
-
-        # 构建帖子数据用于模态框(包含评估信息)
-        eval_data = {
-            'reason': note.get("reason", ""),
-            'title_relevance': note.get("title_relevance", 0),
-            'content_expectation': note.get("content_expectation", 0),
-            'confidence_score': note.get('confidence_score', 0)
-        }
-        post_data = build_post_json_data(note, eval_data)
-        images_json = json.dumps(images)
-
-        image_html = f'<img src="{cover_url}" class="post-image" alt="{note.get("title", "")}">' if cover_url else '<div class="no-image">无图片</div>'
-
-        # 类型标识
-        type_badge = ""
-        if note.get("type") == "video":
-            type_badge = '<div class="post-type-badge">📹 视频</div>'
-
-        # 轮播箭头按钮
-        arrows_html = ""
-        if len(images) > 1:
-            arrows_html = '''
-        <button class="carousel-arrow left" onclick="event.stopPropagation()">‹</button>
-        <button class="carousel-arrow right" onclick="event.stopPropagation()">›</button>
-    '''
-
-        # 评估详情
-        eval_reason = note.get("reason", "")
-        title_rel = note.get("title_relevance", 0)
-        content_exp = note.get("content_expectation", 0)
-
-        eval_details = ""
-
-        # 置信度百分比
-        note_confidence = note.get('confidence_score', 0)
-        confidence_percent = int(note_confidence * 100)
-
-        cited_html += f"""
-        <div class="post-card" onclick='openModal({post_data})' data-images='{images_json}'>
-            <div class="post-image-wrapper">
-                {image_html}
-                {type_badge}
-                {arrows_html}
-            </div>
-            <div class="post-info">
-                <div class="post-title">[{note.get('index')}] {note.get('title', '无标题')}</div>
-                <div class="post-desc">{note.get('desc', '')}</div>
-                <div class="post-meta">
-                    <div class="post-meta-item">❤️ {interact.get('liked_count', 0)}</div>
-                    <div class="post-meta-item">⭐ {interact.get('collected_count', 0)}</div>
-                    <div class="post-meta-item">💬 {interact.get('comment_count', 0)}</div>
-                </div>
-                <div class="post-author">👤 {user.get('nickname', '未知')}</div>
-                <div class="post-id">{note.get('note_id', '')}</div>
-            </div>
-            <div class="confidence-bar">
-                <div class="confidence-bar-fill {get_confidence_class(note_confidence)}" style="width: {confidence_percent}%">
-                    <span class="confidence-bar-text">置信度: {note_confidence:.2f}</span>
-                </div>
-                {eval_details}
-            </div>
-        </div>
-        """
-
-    # 使用可折叠区域包装引用的帖子
-    step_num = step['step_number']
-    cited_section = ""
-    if cited_html:
-        cited_section = make_collapsible(
-            f"📌 引用的帖子 ({len(cited_notes)} 个)",
-            f'<div class="posts-grid">{cited_html}</div>',
-            collapsed=True,
-            section_id=f"step{step_num}-cited"
-        )
-
-    html = f"""
-    <div class="step-section">
-        <div class="step-header">
-            <div class="step-title">步骤 {step['step_number']}: 生成答案</div>
-            <div class="step-type">{step['step_type']}</div>
-        </div>
-        <div class="step-content">
-            <div class="answer-box">
-                <div class="answer-header">📝 生成的答案</div>
-                <div class="answer-content">{answer}</div>
-                <div class="answer-meta">
-                    <div><strong>置信度:</strong> {confidence:.2f}</div>
-                    <div><strong>引用帖子:</strong> {len(cited_notes)} 个</div>
-                </div>
-            </div>
-            {f'<p style="margin-top: 15px; color: #666;"><strong>摘要:</strong> {summary}</p>' if summary else ''}
-            {cited_section}
-        </div>
-        <div class="timestamp">⏰ {step.get('timestamp', '')}</div>
-    </div>
-    """
-    return html
-
-
-def render_final_result(step):
-    """渲染最终结果步骤"""
-    data = step.get("data", {})
-    success = data.get("success", False)
-    message = data.get("message", "")
-    satisfied_notes_count = data.get("satisfied_notes_count", 0)
-
-    status_color = "#10b981" if success else "#ef4444"
-    status_text = "✅ 成功" if success else "❌ 失败"
-
-    html = f"""
-    <div class="step-section" style="border: 3px solid {status_color};">
-        <div class="step-header">
-            <div class="step-title">步骤 {step['step_number']}: {step['step_name']}</div>
-            <div class="step-type">{step['step_type']}</div>
-        </div>
-        <div class="step-content">
-            <div class="info-grid">
-                <div class="info-item" style="background: {status_color}20;">
-                    <div class="info-label">状态</div>
-                    <div class="info-value" style="color: {status_color};">{status_text}</div>
-                </div>
-                <div class="info-item">
-                    <div class="info-label">满足需求的帖子</div>
-                    <div class="info-value">{satisfied_notes_count}</div>
-                </div>
-            </div>
-            <p style="margin-top: 20px; font-size: 15px; color: #666;">{message}</p>
-        </div>
-        <div class="timestamp">⏰ {step.get('timestamp', '')}</div>
-    </div>
-    """
-    return html
-
-
-def render_query_suggestion_evaluation(step):
-    """渲染候选query推荐词评估步骤"""
-    data = step.get("data", {})
-    candidate_count = data.get("candidate_count", 0)
-    results = data.get("results", [])
-
-    results_html = ""
-    step_num = step['step_number']
-    for idx, result in enumerate(results):
-        candidate = result.get("candidate", "")
-        suggestions = result.get("suggestions", [])
-        evaluations = result.get("evaluations", [])
-
-        # 渲染每个候选词的推荐词评估
-        eval_cards = ""
-        for evaluation in evaluations:
-            query = evaluation.get("query", "")
-            intent_match = evaluation.get("intent_match", False)
-            relevance_score = evaluation.get("relevance_score", 0)
-            reason = evaluation.get("reason", "")
-
-            intent_badge = "✅ 意图匹配" if intent_match else "❌ 意图不匹配"
-            intent_class = "confidence-high" if intent_match else "confidence-low"
-
-            eval_cards += f"""
-            <div class="query-item" style="margin: 10px 0; padding: 15px; background: white; border: 1px solid #e5e7eb; border-radius: 8px;">
-                <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
-                    <div class="query-text" style="flex: 1;">{query}</div>
-                    <div style="display: flex; gap: 10px; align-items: center;">
-                        <span class="confidence-badge {intent_class}" style="margin: 0;">{intent_badge}</span>
-                        <span class="confidence-badge confidence-medium" style="margin: 0;">相关性: {relevance_score:.2f}</span>
-                    </div>
-                </div>
-                <div style="color: #666; font-size: 13px; line-height: 1.6; background: #f8f9fa; padding: 10px; border-radius: 4px;">
-                    {reason}
-                </div>
-            </div>
-            """
-
-        if eval_cards:
-            # 使用可折叠区域包装每个候选词的推荐词列表,添加唯一ID
-            results_html += make_collapsible(
-                f"候选词: {candidate} ({len(evaluations)} 个推荐词)",
-                eval_cards,
-                collapsed=True,
-                section_id=f"step{step_num}-candidate-{idx}"
-            )
-
-    html = f"""
-    <div class="step-section">
-        <div class="step-header">
-            <div class="step-title">步骤 {step['step_number']}: {step['step_name']}</div>
-            <div class="step-type">{step['step_type']}</div>
-        </div>
-        <div class="step-content">
-            <div class="info-grid">
-                <div class="info-item">
-                    <div class="info-label">候选query数</div>
-                    <div class="info-value">{candidate_count}</div>
-                </div>
-                <div class="info-item">
-                    <div class="info-label">总推荐词数</div>
-                    <div class="info-value">{sum(len(r.get('evaluations', [])) for r in results)}</div>
-                </div>
-            </div>
-            {results_html}
-        </div>
-        <div class="timestamp">⏰ {step.get('timestamp', '')}</div>
-    </div>
-    """
-    return html
-
-
-def render_filter_qualified_queries(step):
-    """渲染筛选合格推荐词步骤"""
-    data = step.get("data", {})
-    input_count = data.get("input_evaluation_count", 0)
-    qualified_count = data.get("qualified_count", 0)
-    min_relevance = data.get("min_relevance_score", 0.7)
-    all_queries = data.get("all_queries", [])
-
-    # 如果没有all_queries,使用旧的qualified_queries
-    if not all_queries:
-        all_queries = data.get("qualified_queries", [])
-
-    # 分离合格和不合格的查询
-    qualified_html = ""
-    unqualified_html = ""
-
-    for item in all_queries:
-        query = item.get("query", "")
-        from_candidate = item.get("from_candidate", "")
-        intent_match = item.get("intent_match", False)
-        relevance_score = item.get("relevance_score", 0)
-        reason = item.get("reason", "")
-        is_qualified = item.get("is_qualified", True)  # 默认为True以兼容旧数据
-
-        intent_badge = "✅ 意图匹配" if intent_match else "❌ 意图不匹配"
-        intent_class = "confidence-high" if intent_match else "confidence-low"
-
-        # 根据相关性分数确定badge颜色
-        if relevance_score >= 0.8:
-            score_class = "confidence-high"
-        elif relevance_score >= 0.6:
-            score_class = "confidence-medium"
-        else:
-            score_class = "confidence-low"
-
-        # 确定边框颜色和背景色
-        if is_qualified:
-            border_color = "#10b981"
-            bg_color = "#f0fdf4"
-            border_left_color = "#10b981"
-        else:
-            border_color = "#e5e7eb"
-            bg_color = "#f9fafb"
-            border_left_color = "#9ca3af"
-
-        query_html = f"""
-        <div class="query-item" style="margin: 15px 0; padding: 15px; background: white; border: 2px solid {border_color}; border-radius: 8px;">
-            <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
-                <div style="flex: 1;">
-                    <div class="query-text">{query}</div>
-                    <div style="color: #9ca3af; font-size: 12px; margin-top: 5px;">来自候选词: {from_candidate}</div>
-                </div>
-                <div style="display: flex; gap: 10px; align-items: center;">
-                    <span class="confidence-badge {intent_class}" style="margin: 0;">{intent_badge}</span>
-                    <span class="confidence-badge {score_class}" style="margin: 0;">相关性: {relevance_score:.2f}</span>
-                </div>
-            </div>
-            <div style="color: #666; font-size: 13px; line-height: 1.6; background: {bg_color}; padding: 10px; border-radius: 4px; border-left: 3px solid {border_left_color};">
-                {reason}
-            </div>
-        </div>
-        """
-
-        if is_qualified:
-            qualified_html += query_html
-        else:
-            unqualified_html += query_html
-
-    # 构建HTML - 使用可折叠区域
-    step_num = step['step_number']
-    qualified_section = make_collapsible(
-        f"✅ 合格的推荐词 ({qualified_count})",
-        qualified_html,
-        collapsed=True,
-        section_id=f"step{step_num}-qualified"
-    ) if qualified_html else ''
-
-    unqualified_section = make_collapsible(
-        f"❌ 不合格的推荐词 ({input_count - qualified_count})",
-        unqualified_html,
-        collapsed=True,
-        section_id=f"step{step_num}-unqualified"
-    ) if unqualified_html else ''
-
-    html = f"""
-    <div class="step-section">
-        <div class="step-header">
-            <div class="step-title">步骤 {step['step_number']}: {step['step_name']}</div>
-            <div class="step-type">{step['step_type']}</div>
-        </div>
-        <div class="step-content">
-            <div class="info-grid">
-                <div class="info-item">
-                    <div class="info-label">输入推荐词数</div>
-                    <div class="info-value">{input_count}</div>
-                </div>
-                <div class="info-item">
-                    <div class="info-label">合格推荐词数</div>
-                    <div class="info-value">{qualified_count}</div>
-                </div>
-                <div class="info-item">
-                    <div class="info-label">最低相关性</div>
-                    <div class="info-value">{min_relevance:.2f}</div>
-                </div>
-            </div>
-            {qualified_section}
-            {unqualified_section}
-        </div>
-        <div class="timestamp">⏰ {step.get('timestamp', '')}</div>
-    </div>
-    """
-    return html
-
-
-def render_generic_step(step):
-    """通用步骤渲染"""
-    data = step.get("data", {})
-
-    # 提取数据的简单展示
-    data_html = ""
-    if data:
-        data_html = "<div class='step-content'><pre style='background: #f8f9fa; padding: 15px; border-radius: 4px; overflow-x: auto; font-size: 12px;'>"
-        import json
-        data_html += json.dumps(data, ensure_ascii=False, indent=2)[:500]  # 限制长度
-        if len(json.dumps(data)) > 500:
-            data_html += "\n..."
-        data_html += "</pre></div>"
-
-    return f"""
-    <div class="step-section">
-        <div class="step-header">
-            <div class="step-title">步骤 {step['step_number']}: {step['step_name']}</div>
-            <div class="step-type">{step['step_type']}</div>
-        </div>
-        {data_html}
-        <div class="timestamp">⏰ {step.get('timestamp', '')}</div>
-    </div>
-    """
-
-
-def render_step(step):
-    """根据步骤类型渲染对应的HTML"""
-    step_type = step.get("step_type", "")
-
-    renderers = {
-        "keyword_extraction": render_keyword_extraction,
-        "level_exploration": render_level_exploration,
-        "level_analysis": render_level_analysis,
-        "query_suggestion_evaluation": render_query_suggestion_evaluation,
-        "filter_qualified_queries": render_filter_qualified_queries,
-        "search_qualified_queries": render_search_results,
-        "evaluate_search_notes": render_note_evaluations,
-        "answer_generation": render_answer_generation,
-        "final_result": render_final_result,
-    }
-
-    renderer = renderers.get(step_type)
-    if renderer:
-        return renderer(step)
-    else:
-        # 使用通用渲染显示数据
-        return render_generic_step(step)
-
-
-def generate_html(steps_json_path, output_path=None):
-    """生成HTML可视化文件"""
-    # 读取 steps.json
-    with open(steps_json_path, 'r', encoding='utf-8') as f:
-        steps_data = json.load(f)
-
-    # 生成内容
-    content_parts = [render_header(steps_data)]
-
-    for step in steps_data:
-        content_parts.append(render_step(step))
-
-    content = "\n".join(content_parts)
-
-    # 生成最终HTML(使用replace而不是format来避免CSS中的花括号问题)
-    html = HTML_TEMPLATE.replace("{content}", content)
-
-    # 确定输出路径
-    if output_path is None:
-        steps_path = Path(steps_json_path)
-        output_path = steps_path.parent / "steps_visualization.html"
-
-    # 写入文件
-    with open(output_path, 'w', encoding='utf-8') as f:
-        f.write(html)
-
-    return output_path
-
-
-def main():
-    parser = argparse.ArgumentParser(description="Steps 可视化工具")
-    parser.add_argument("steps_json", type=str, help="steps.json 文件路径")
-    parser.add_argument("-o", "--output", type=str, help="输出HTML文件路径(可选)")
-
-    args = parser.parse_args()
-
-    # 生成可视化
-    output_path = generate_html(args.steps_json, args.output)
-
-    print(f"✅ 可视化生成成功!")
-    print(f"📄 输出文件: {output_path}")
-    output_abs = Path(output_path).absolute() if isinstance(output_path, str) else output_path.absolute()
-    print(f"\n💡 在浏览器中打开查看: file://{output_abs}")
-
-
-if __name__ == "__main__":
-    main()

+ 0 - 1908
sug_v6_1_2_4.py

@@ -1,1908 +0,0 @@
-import asyncio
-import json
-import os
-import sys
-import argparse
-from datetime import datetime
-
-from agents import Agent, Runner
-from lib.my_trace import set_trace
-from typing import Literal
-from pydantic import BaseModel, Field
-
-from lib.utils import read_file_as_string
-from lib.client import get_model
-MODEL_NAME = "google/gemini-2.5-flash"
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-from script.search.xiaohongshu_search import XiaohongshuSearch
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-
-    # 步骤化日志
-    steps: list[dict] = Field(default_factory=list, description="执行步骤的详细记录")
-
-    # 探索阶段记录(保留用于向后兼容)
-    keywords: list[str] | None = Field(default=None, description="提取的关键词")
-    exploration_levels: list[dict] = Field(default_factory=list, description="每一层的探索结果")
-    level_analyses: list[dict] = Field(default_factory=list, description="每一层的主Agent分析")
-
-    # 最终结果
-    final_candidates: list[str] | None = Field(default=None, description="最终选出的候选query")
-    evaluation_results: list[dict] | None = Field(default=None, description="候选query的评估结果")
-    optimization_result: dict | None = Field(default=None, description="最终优化结果对象")
-    final_output: str | None = Field(default=None, description="最终输出结果(格式化文本)")
-
-
-# ============================================================================
-# Agent 1: 关键词提取专家
-# ============================================================================
-keyword_extraction_instructions = """
-你是关键词提取专家。给定一个搜索问题(含上下文),提取出**最细粒度的关键概念**。
-
-## 提取原则
-
-1. **细粒度优先**:拆分成最小的有意义单元
-   - 不要保留完整的长句
-   - 拆分成独立的、有搜索意义的词或短语
-
-2. **保留核心维度**:
-   - 地域/对象
-   - 时间
-   - 行为/意图:获取、教程、推荐、如何等
-   - 主题/领域
-   - 质量/属性
-
-3. **去掉无意义的虚词**:的、吗、呢等
-
-4. **保留领域专有词**:不要过度拆分专业术语
-   - 如果是常见的组合词,保持完整
-
-## 输出要求
-
-输出关键词列表,按重要性排序(最核心的在前)。
-""".strip()
-
-class KeywordList(BaseModel):
-    """关键词列表"""
-    keywords: list[str] = Field(..., description="提取的关键词,按重要性排序")
-    reasoning: str = Field(..., description="提取理由")
-
-keyword_extractor = Agent[None](
-    name="关键词提取专家",
-    instructions=keyword_extraction_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=KeywordList,
-)
-
-
-# ============================================================================
-# Agent 2: 层级探索分析专家
-# ============================================================================
-level_analysis_instructions = """
-你是搜索空间探索分析专家。基于当前层级的探索结果,决定下一步行动。
-
-## 你的任务
-
-分析当前已探索的词汇空间,判断:
-1. **发现了什么有价值的信号?**
-2. **是否已经可以评估候选了?**
-3. **如果还不够,下一层应该探索什么组合?**
-
-## 分析维度
-
-### 1. 信号识别(最重要)
-
-看推荐词里**出现了什么主题**:
-
-**关键问题:**
-- 哪些推荐词**最接近原始需求**?
-- 哪些推荐词**揭示了有价值的方向**(即使不完全匹配)?
-- 哪些推荐词可以作为**下一层探索的桥梁**?
-- 系统对哪些概念理解得好?哪些理解偏了?
-
-### 2. 组合策略
-
-基于发现的信号,设计下一层探索:
-
-**组合类型:**
-
-a) **关键词直接组合**
-   - 两个关键词组合成新query
-
-b) **利用推荐词作为桥梁**(重要!)
-   - 发现某个推荐词很有价值 → 直接探索这个推荐词
-   - 或在推荐词基础上加其他关键词
-
-c) **跨层级组合**
-   - 结合多层发现的有价值推荐词
-   - 组合成更复杂的query
-
-### 3. 停止条件
-
-**何时可以评估候选?**
-
-满足以下之一:
-- 推荐词中出现了**明确包含原始需求多个核心要素的query**
-- 已经探索到**足够复杂的组合**(3-4个关键词),且推荐词相关
-- 探索了**3-4层**,信息已经足够丰富
-
-**何时继续探索?**
-- 当前推荐词太泛,没有接近原始需求
-- 发现了有价值的信号,但需要进一步组合验证
-- 层数还少(< 3层)
-
-## 输出要求
-
-### 1. key_findings
-总结当前层发现的关键信息,包括:
-- 哪些推荐词最有价值?
-- 系统对哪些概念理解得好/不好?
-- 发现了什么意外的方向?
-
-### 2. promising_signals
-列出最有价值的推荐词(来自任何已探索的query),每个说明为什么有价值
-
-### 3. should_evaluate_now
-是否已经可以开始评估候选了?true/false
-
-### 4. candidates_to_evaluate
-如果should_evaluate_now=true,列出应该评估的候选query
-- 可以是推荐词
-- 可以是自己构造的组合
-
-### 5. next_combinations
-如果should_evaluate_now=false,列出下一层应该探索的query组合
-
-### 6. reasoning
-详细的推理过程
-
-## 重要原则
-
-1. **不要过早评估**:至少探索2层,除非第一层就发现了完美匹配
-2. **充分利用推荐词**:推荐词是系统给的提示,要善用
-3. **保持探索方向的多样性**:不要只盯着一个方向
-4. **识别死胡同**:如果某个方向的推荐词一直不相关,果断放弃
-""".strip()
-
-class PromisingSignal(BaseModel):
-    """有价值的推荐词信号"""
-    query: str = Field(..., description="推荐词")
-    from_level: int = Field(..., description="来自哪一层")
-    reason: str = Field(..., description="为什么有价值")
-
-class LevelAnalysis(BaseModel):
-    """层级分析结果"""
-    key_findings: str = Field(..., description="当前层的关键发现")
-    promising_signals: list[PromisingSignal] = Field(..., description="有价值的推荐词信号")
-    should_evaluate_now: bool = Field(..., description="是否应该开始评估候选")
-    candidates_to_evaluate: list[str] = Field(default_factory=list, description="如果should_evaluate_now=true,要评估的候选query列表")
-    next_combinations: list[str] = Field(default_factory=list, description="如果should_evaluate_now=false,下一层要探索的query组合")
-    reasoning: str = Field(..., description="详细的推理过程")
-
-level_analyzer = Agent[None](
-    name="层级探索分析专家",
-    instructions=level_analysis_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=LevelAnalysis,
-)
-
-
-# ============================================================================
-# Agent 3: 评估专家(简化版:意图匹配 + 相关性评分)
-# ============================================================================
-eval_instructions = """
-你是搜索query评估专家。给定原始问题和推荐query,评估两个维度。
-
-## 评估目标
-
-用这个推荐query搜索,能否找到满足原始需求的内容?
-
-## 两层评分
-
-### 1. intent_match(意图匹配)= true/false
-
-推荐query的**使用意图**是否与原问题一致?
-
-**核心问题:用户搜索这个推荐词,想做什么?**
-
-**判断标准:**
-- 原问题意图:找方法?找教程?找资源/素材?找工具?看作品?
-- 推荐词意图:如果用户搜索这个词,他的目的是什么?
-
-**示例:**
-- 原问题意图="找素材"
-  - ✅ true: "素材下载"、"素材网站"、"免费素材"(都是获取素材)
-  - ❌ false: "素材制作教程"、"如何制作素材"(意图变成学习了)
-
-- 原问题意图="学教程"
-  - ✅ true: "教程视频"、"教学步骤"、"入门指南"
-  - ❌ false: "成品展示"、"作品欣赏"(意图变成看作品了)
-
-**评分:**
-- true = 意图一致,搜索推荐词能达到原问题的目的
-- false = 意图改变,搜索推荐词无法达到原问题的目的
-
-### 2. relevance_score(相关性)= 0-1 连续分数
-
-推荐query在**主题、要素、属性**上与原问题的相关程度?
-
-**评估维度:**
-- 主题相关:核心主题是否匹配?(如:摄影、旅游、美食)
-- 要素覆盖:关键要素保留了多少?(如:地域、时间、对象、工具)
-- 属性匹配:质量、风格、特色等属性是否保留?
-
-**评分参考:**
-- 0.9-1.0 = 几乎完美匹配,所有核心要素都保留
-- 0.7-0.8 = 高度相关,核心要素保留,少数次要要素缺失
-- 0.5-0.6 = 中度相关,主题匹配但多个要素缺失
-- 0.3-0.4 = 低度相关,只有部分主题相关
-- 0-0.2 = 基本不相关
-
-## 评估策略
-
-1. **先判断 intent_match**:意图不匹配直接 false,无论相关性多高
-2. **再评估 relevance_score**:在意图匹配的前提下,计算相关性
-
-## 输出要求
-
-- intent_match: true/false
-- relevance_score: 0-1 的浮点数
-- reason: 详细的评估理由,需要说明:
-  - 原问题的意图是什么
-  - 推荐词的意图是什么
-  - 为什么判断意图匹配/不匹配
-  - 相关性分数的依据(哪些要素保留/缺失)
-""".strip()
-
-class RelevanceEvaluation(BaseModel):
-    """评估反馈模型 - 意图匹配 + 相关性"""
-    intent_match: bool = Field(..., description="意图是否匹配")
-    relevance_score: float = Field(..., description="相关性分数 0-1,分数越高越相关")
-    reason: str = Field(..., description="评估理由,需说明意图判断和相关性依据")
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=RelevanceEvaluation,
-)
-
-
-# ============================================================================
-# Agent 4: 单个帖子需求满足度评估专家
-# ============================================================================
-note_evaluation_instructions = """
-你是帖子需求满足度评估专家。给定原始问题和一个搜索到的帖子(标题+描述),判断这个帖子能否满足用户的需求。
-
-## 你的任务
-
-评估单个帖子的标题和描述,判断用户点开这个帖子后,能否找到满足原始需求的内容。
-
-## 评估维度
-
-### 1. 标题相关性(title_relevance)= 0-1 连续分数
-
-**评估标准:**
-- 标题是否与原始问题的主题相关?
-- 标题是否包含原始问题的关键要素?
-- 标题是否表明内容能解决用户的问题?
-
-**评分参考:**
-- 0.9-1.0 = 标题高度相关,明确表明能解决问题
-- 0.7-0.8 = 标题相关,包含核心要素
-- 0.5-0.6 = 标题部分相关,有关联但不明确
-- 0.3-0.4 = 标题相关性较低
-- 0-0.2 = 标题基本不相关
-
-### 2. 内容预期(content_expectation)= 0-1 连续分数
-
-**评估标准:**
-- 从描述看,内容是否可能包含用户需要的信息?
-- 描述是否展示了相关的要素或细节?
-- 描述的方向是否与用户需求一致?
-
-**评分参考:**
-- 0.9-1.0 = 描述明确表明内容高度符合需求
-- 0.7-0.8 = 描述显示内容可能符合需求
-- 0.5-0.6 = 描述有一定相关性,但不确定
-- 0.3-0.4 = 描述相关性较低
-- 0-0.2 = 描述基本不相关
-
-### 3. 需求满足度(need_satisfaction)= true/false
-
-**核心问题:用户点开这个帖子后,能否找到他需要的内容?**
-
-**判断标准:**
-- 综合标题和描述,内容是否大概率能满足需求?
-- 如果 title_relevance >= 0.7 且 content_expectation >= 0.6,一般判断为 true
-- 否则判断为 false
-
-### 4. 综合置信度(confidence_score)= 0-1 连续分数
-
-**计算方式:**
-- 可以是 title_relevance 和 content_expectation 的加权平均
-- 标题权重通常更高(如 0.6 * title + 0.4 * content)
-
-## 输出要求
-
-- title_relevance: 0-1 的浮点数
-- content_expectation: 0-1 的浮点数
-- need_satisfaction: true/false
-- confidence_score: 0-1 的浮点数
-- reason: 详细的评估理由,需要说明:
-  - 标题与原问题的相关性分析
-  - 描述的内容预期分析
-  - 为什么判断能/不能满足需求
-  - 置信度分数的依据
-
-## 重要原则
-
-1. **独立评估**:只评估这一个帖子,不考虑其他帖子
-2. **用户视角**:问"我会点开这个帖子吗?点开后能找到答案吗?"
-3. **标题优先**:标题是用户决定是否点击的关键
-4. **保守判断**:不确定时,倾向于给较低的分数
-""".strip()
-
-class NoteEvaluation(BaseModel):
-    """单个帖子评估模型"""
-    title_relevance: float = Field(..., description="标题相关性 0-1")
-    content_expectation: float = Field(..., description="内容预期 0-1")
-    need_satisfaction: bool = Field(..., description="是否满足需求")
-    confidence_score: float = Field(..., description="综合置信度 0-1")
-    reason: str = Field(..., description="详细的评估理由")
-
-note_evaluator = Agent[None](
-    name="帖子需求满足度评估专家",
-    instructions=note_evaluation_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=NoteEvaluation,
-)
-
-
-# ============================================================================
-# Agent 5: 答案生成专家
-# ============================================================================
-answer_generation_instructions = """
-你是答案生成专家。基于一组满足需求的帖子,为原始问题生成一个全面、准确、有价值的答案。
-
-## 你的任务
-
-根据用户的原始问题和一组相关帖子(包含标题、描述、置信度评分),生成一个高质量的答案。
-
-## 输入信息
-
-1. **原始问题**:用户提出的具体问题
-2. **相关帖子列表**:每个帖子包含
-   - 序号(索引)
-   - 标题
-   - 描述
-   - 置信度分数
-
-## 答案要求
-
-### 1. 内容要求
-
-- **直接回答问题**:开门见山,第一段就给出核心答案
-- **结构清晰**:使用标题、列表、分段等组织内容
-- **综合多个来源**:整合多个帖子的信息,不要只依赖一个
-- **信息准确**:基于帖子内容,不要编造信息
-- **实用性**:提供可操作的建议或具体的信息
-
-### 2. 引用规范
-
-- **必须标注来源**:每个关键信息都要标注帖子索引
-- **引用格式**:使用 `[1]`、`[2]` 等标注帖子序号
-- **多来源引用**:如果多个帖子支持同一观点,使用 `[1,2,3]`
-- **引用位置**:在相关句子或段落的末尾标注
-
-### 3. 置信度使用
-
-- **优先高置信度**:优先引用置信度高的帖子
-- **交叉验证**:如果多个帖子提到相同信息,可以提高可信度
-- **标注不确定性**:如果信息来自低置信度帖子,适当标注
-
-### 4. 答案结构建议
-
-```
-【核心答案】
-直接回答用户的问题,给出最核心的信息。[引用]
-
-【详细说明】
-1. 第一个方面/要点 [引用]
-   - 具体内容
-   - 相关细节
-
-2. 第二个方面/要点 [引用]
-   - 具体内容
-   - 相关细节
-
-【补充建议/注意事项】(可选)
-其他有价值的信息或提醒。[引用]
-
-【参考帖子】
-列出所有引用的帖子编号和标题。
-```
-
-## 输出格式
-
-{
-  "answer": "生成的答案内容(Markdown格式)",
-  "cited_note_indices": [1, 2, 3],  # 引用的帖子序号列表
-  "confidence": 0.85,  # 答案的整体置信度 (0-1)
-  "summary": "一句话总结答案的核心内容"
-}
-
-## 重要原则
-
-1. **忠于原文**:不要添加帖子中没有的信息
-2. **引用透明**:让用户知道每个信息来自哪个帖子
-3. **综合性**:尽可能整合多个帖子的信息
-4. **可读性**:使用清晰的Markdown格式
-5. **质量优先**:如果帖子质量不够,可以说明信息有限
-""".strip()
-
-class AnswerGeneration(BaseModel):
-    """答案生成模型"""
-    answer: str = Field(..., description="生成的答案内容(Markdown格式)")
-    cited_note_indices: list[int] = Field(..., description="引用的帖子序号列表")
-    confidence: float = Field(..., description="答案的整体置信度 0-1")
-    summary: str = Field(..., description="一句话总结答案的核心内容")
-
-answer_generator = Agent[None](
-    name="答案生成专家",
-    instructions=answer_generation_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=AnswerGeneration,
-)
-
-
-# ============================================================================
-# 日志辅助函数
-# ============================================================================
-
-def add_step(context: RunContext, step_name: str, step_type: str, data: dict):
-    """添加步骤记录"""
-    step = {
-        "step_number": len(context.steps) + 1,
-        "step_name": step_name,
-        "step_type": step_type,
-        "timestamp": datetime.now().isoformat(),
-        "data": data
-    }
-    context.steps.append(step)
-    return step
-
-
-def process_note_data(note: dict) -> dict:
-    """
-    处理搜索接口返回的帖子数据,标准化为统一格式
-
-    Args:
-        note: 搜索接口返回的原始帖子数据
-
-    Returns:
-        标准化后的帖子数据字典
-    """
-    note_card = note.get("note_card", {})
-    image_list = note_card.get("image_list", [])  # 已在搜索API层预处理为URL字符串列表
-    interact_info = note_card.get("interact_info", {})
-    user_info = note_card.get("user", {})
-
-    return {
-        "note_id": note.get("id", ""),
-        "title": note_card.get("display_title", ""),
-        "desc": note_card.get("desc", ""),
-        "image_list": image_list,  # 第一张就是封面,已在XiaohongshuSearch.search()中预处理为URL字符串列表
-        "interact_info": {
-            "liked_count": interact_info.get("liked_count", 0),
-            "collected_count": interact_info.get("collected_count", 0),
-            "comment_count": interact_info.get("comment_count", 0),
-            "shared_count": interact_info.get("shared_count", 0)
-        },
-        "user": {
-            "nickname": user_info.get("nickname", ""),
-            "user_id": user_info.get("user_id", "")
-        },
-        "type": note_card.get("type", "normal"),
-        "note_url": f"https://www.xiaohongshu.com/explore/{note.get('id', '')}"
-    }
-
-
-# ============================================================================
-# 核心函数
-# ============================================================================
-
-async def extract_keywords(q: str, context: RunContext) -> KeywordList:
-    """提取关键词"""
-    print("\n[步骤 1] 正在提取关键词...")
-    result = await Runner.run(keyword_extractor, q)
-    keyword_list: KeywordList = result.final_output
-    print(f"提取的关键词:{keyword_list.keywords}")
-    print(f"提取理由:{keyword_list.reasoning}")
-
-    # 记录步骤
-    add_step(context, "提取关键词", "keyword_extraction", {
-        "input_question": q,
-        "keywords": keyword_list.keywords,
-        "reasoning": keyword_list.reasoning
-    })
-
-    return keyword_list
-
-
-async def explore_level(queries: list[str], level_num: int, context: RunContext) -> dict:
-    """探索一个层级(并发获取所有query的推荐词)"""
-    step_num = len(context.steps) + 1
-    print(f"\n{'='*60}")
-    print(f"[步骤 {step_num}] Level {level_num} 探索:{len(queries)} 个query")
-    print(f"{'='*60}")
-
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-
-    # 并发获取所有推荐词
-    async def get_single_sug(query: str):
-        print(f"  探索: {query}")
-        suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-        print(f"    → {len(suggestions) if suggestions else 0} 个推荐词")
-        return {
-            "query": query,
-            "suggestions": suggestions or []
-        }
-
-    results = await asyncio.gather(*[get_single_sug(q) for q in queries])
-
-    level_data = {
-        "level": level_num,
-        "timestamp": datetime.now().isoformat(),
-        "queries": results
-    }
-
-    context.exploration_levels.append(level_data)
-
-    # 记录步骤
-    add_step(context, f"Level {level_num} 探索", "level_exploration", {
-        "level": level_num,
-        "input_queries": queries,
-        "query_count": len(queries),
-        "results": results,
-        "total_suggestions": sum(len(r['suggestions']) for r in results)
-    })
-
-    return level_data
-
-
-async def analyze_level(level_data: dict, all_levels: list[dict], original_question: str, context: RunContext) -> LevelAnalysis:
-    """分析当前层级,决定下一步"""
-    step_num = len(context.steps) + 1
-    print(f"\n[步骤 {step_num}] 正在分析 Level {level_data['level']}...")
-
-    # 构造输入
-    analysis_input = f"""
-<原始问题>
-{original_question}
-</原始问题>
-
-<已探索的所有层级>
-{json.dumps(all_levels, ensure_ascii=False, indent=2)}
-</已探索的所有层级>
-
-<当前层级>
-Level {level_data['level']}
-{json.dumps(level_data['queries'], ensure_ascii=False, indent=2)}
-</当前层级>
-
-请分析当前探索状态,决定下一步行动。
-"""
-
-    result = await Runner.run(level_analyzer, analysis_input)
-    analysis: LevelAnalysis = result.final_output
-
-    print(f"\n分析结果:")
-    print(f"  关键发现:{analysis.key_findings}")
-    print(f"  有价值的信号:{len(analysis.promising_signals)} 个")
-    print(f"  是否评估:{analysis.should_evaluate_now}")
-
-    if analysis.should_evaluate_now:
-        print(f"  候选query:{analysis.candidates_to_evaluate}")
-    else:
-        print(f"  下一层探索:{analysis.next_combinations}")
-
-    # 保存分析结果
-    context.level_analyses.append({
-        "level": level_data['level'],
-        "timestamp": datetime.now().isoformat(),
-        "analysis": analysis.model_dump()
-    })
-
-    # 记录步骤
-    add_step(context, f"Level {level_data['level']} 分析", "level_analysis", {
-        "level": level_data['level'],
-        "key_findings": analysis.key_findings,
-        "promising_signals_count": len(analysis.promising_signals),
-        "promising_signals": [s.model_dump() for s in analysis.promising_signals],
-        "should_evaluate_now": analysis.should_evaluate_now,
-        "candidates_to_evaluate": analysis.candidates_to_evaluate if analysis.should_evaluate_now else [],
-        "next_combinations": analysis.next_combinations if not analysis.should_evaluate_now else [],
-        "reasoning": analysis.reasoning
-    })
-
-    return analysis
-
-
-async def evaluate_candidates(candidates: list[str], original_question: str, context: RunContext) -> list[dict]:
-    """评估候选query(含实际搜索验证)"""
-    step_num = len(context.steps) + 1
-    print(f"\n{'='*60}")
-    print(f"[步骤 {step_num}] 评估 {len(candidates)} 个候选query")
-    print(f"{'='*60}")
-
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-    xiaohongshu_search = XiaohongshuSearch()
-
-    # 创建搜索结果保存目录
-    search_results_dir = os.path.join(context.log_dir, "search_results")
-    os.makedirs(search_results_dir, exist_ok=True)
-
-    async def evaluate_single_candidate(candidate: str, candidate_index: int):
-        print(f"\n评估候选:{candidate}")
-
-        # 为当前候选创建子目录
-        # 清理候选名称,移除不适合作为目录名的字符
-        safe_candidate_name = "".join(c if c.isalnum() or c in (' ', '_', '-') else '_' for c in candidate)
-        candidate_dir = os.path.join(search_results_dir, f"candidate_{candidate_index+1}_{safe_candidate_name[:50]}")
-        os.makedirs(candidate_dir, exist_ok=True)
-
-        # 1. 获取推荐词
-        suggestions = xiaohongshu_api.get_recommendations(keyword=candidate)
-        print(f"  获取到 {len(suggestions) if suggestions else 0} 个推荐词")
-
-        if not suggestions:
-            return {
-                "candidate": candidate,
-                "suggestions": [],
-                "evaluations": []
-            }
-
-        # 2. 评估每个推荐词(意图匹配 + 相关性)
-        async def eval_single_sug(sug: str, sug_index: int):
-            # 2.1 先进行意图和相关性评估
-            eval_input = f"""
-<原始问题>
-{original_question}
-</原始问题>
-
-<待评估的推荐query>
-{sug}
-</待评估的推荐query>
-
-请评估该推荐query:
-1. intent_match: 意图是否匹配(true/false)
-2. relevance_score: 相关性分数(0-1)
-3. reason: 详细的评估理由
-"""
-            result = await Runner.run(evaluator, eval_input)
-            evaluation: RelevanceEvaluation = result.final_output
-
-            eval_result = {
-                "query": sug,
-                "intent_match": evaluation.intent_match,
-                "relevance_score": evaluation.relevance_score,
-                "reason": evaluation.reason,
-            }
-
-            # 2.2 如果意图匹配且相关性足够高,进行实际搜索验证
-            if evaluation.intent_match and evaluation.relevance_score >= 0.7:
-                print(f"    → 合格候选,进行实际搜索验证: {sug}")
-                try:
-                    search_result = xiaohongshu_search.search(keyword=sug)
-
-                    # 解析result字段(它是JSON字符串,需要先解析)
-                    result_str = search_result.get("result", "{}")
-                    if isinstance(result_str, str):
-                        result_data = json.loads(result_str)
-                    else:
-                        result_data = result_str
-
-                    # 格式化搜索结果:将result字段解析后再保存
-                    formatted_search_result = {
-                        "success": search_result.get("success"),
-                        "result": result_data,  # 保存解析后的数据
-                        "tool_name": search_result.get("tool_name"),
-                        "call_type": search_result.get("call_type"),
-                        "query": sug,
-                        "timestamp": datetime.now().isoformat()
-                    }
-
-                    # 保存格式化后的搜索结果到文件
-                    safe_sug_name = "".join(c if c.isalnum() or c in (' ', '_', '-') else '_' for c in sug)
-                    search_result_file = os.path.join(candidate_dir, f"sug_{sug_index+1}_{safe_sug_name[:30]}.json")
-                    with open(search_result_file, 'w', encoding='utf-8') as f:
-                        json.dump(formatted_search_result, f, ensure_ascii=False, indent=2)
-                    print(f"       搜索结果已保存: {os.path.basename(search_result_file)}")
-
-                    # 提取搜索结果的标题和描述
-                    # 正确的数据路径: result.data.data[]
-                    notes = result_data.get("data", {}).get("data", [])
-                    if notes:
-                        print(f"       开始评估 {len(notes)} 个帖子...")
-
-                        # 对每个帖子进行独立评估
-                        note_evaluations = []
-                        for note_idx, note in enumerate(notes, 1):  # 评估所有帖子
-                            note_card = note.get("note_card", {})
-                            title = note_card.get("display_title", "")
-                            desc = note_card.get("desc", "")
-                            note_id = note.get("id", "")
-
-                            # 构造评估输入
-                            eval_input = f"""
-<原始问题>
-{original_question}
-</原始问题>
-
-<帖子信息>
-标题: {title}
-描述: {desc}
-</帖子信息>
-
-请评估这个帖子能否满足用户需求。
-"""
-                            # 调用评估Agent
-                            eval_result_run = await Runner.run(note_evaluator, eval_input)
-                            note_eval: NoteEvaluation = eval_result_run.final_output
-
-                            note_evaluation_record = {
-                                "note_index": note_idx,
-                                "note_id": note_id,
-                                "title": title,
-                                "desc": desc,  # 保存完整描述
-                                "evaluation": {
-                                    "title_relevance": note_eval.title_relevance,
-                                    "content_expectation": note_eval.content_expectation,
-                                    "need_satisfaction": note_eval.need_satisfaction,
-                                    "confidence_score": note_eval.confidence_score,
-                                    "reason": note_eval.reason
-                                }
-                            }
-                            note_evaluations.append(note_evaluation_record)
-
-                            # 简单打印进度
-                            if note_idx % 3 == 0 or note_idx == len(notes):
-                                print(f"         已评估 {note_idx}/{len(notes)} 个帖子")
-
-                        # 统计满足需求的帖子数量
-                        satisfied_count = sum(1 for ne in note_evaluations if ne["evaluation"]["need_satisfaction"])
-                        avg_confidence = sum(ne["evaluation"]["confidence_score"] for ne in note_evaluations) / len(note_evaluations) if note_evaluations else 0
-
-                        eval_result["search_verification"] = {
-                            "total_notes": len(notes),
-                            "evaluated_notes": len(note_evaluations),
-                            "satisfied_count": satisfied_count,
-                            "average_confidence": round(avg_confidence, 2),
-                            "note_evaluations": note_evaluations,
-                            "search_result_file": search_result_file
-                        }
-
-                        print(f"       评估完成: {satisfied_count}/{len(note_evaluations)} 个帖子满足需求, "
-                              f"平均置信度={avg_confidence:.2f}")
-                    else:
-                        eval_result["search_verification"] = {
-                            "total_notes": 0,
-                            "evaluated_notes": 0,
-                            "satisfied_count": 0,
-                            "average_confidence": 0.0,
-                            "note_evaluations": [],
-                            "search_result_file": search_result_file,
-                            "reason": "搜索无结果"
-                        }
-                        print(f"       搜索无结果")
-
-                except Exception as e:
-                    print(f"       搜索验证出错: {e}")
-                    eval_result["search_verification"] = {
-                        "error": str(e)
-                    }
-
-            return eval_result
-
-        evaluations = await asyncio.gather(*[eval_single_sug(s, i) for i, s in enumerate(suggestions)])
-
-        return {
-            "candidate": candidate,
-            "suggestions": suggestions,
-            "evaluations": evaluations
-        }
-
-    results = await asyncio.gather(*[evaluate_single_candidate(c, i) for i, c in enumerate(candidates)])
-
-    # 生成搜索结果汇总文件
-    summary_data = {
-        "original_question": original_question,
-        "timestamp": datetime.now().isoformat(),
-        "total_candidates": len(candidates),
-        "candidates": []
-    }
-
-    for i, result in enumerate(results):
-        candidate_summary = {
-            "index": i + 1,
-            "candidate": result["candidate"],
-            "suggestions_count": len(result["suggestions"]),
-            "verified_queries": []
-        }
-
-        for eval_item in result.get("evaluations", []):
-            if "search_verification" in eval_item and "search_result_file" in eval_item["search_verification"]:
-                sv = eval_item["search_verification"]
-                candidate_summary["verified_queries"].append({
-                    "query": eval_item["query"],
-                    "intent_match": eval_item["intent_match"],
-                    "relevance_score": eval_item["relevance_score"],
-                    "verification": {
-                        "total_notes": sv.get("total_notes", 0),
-                        "evaluated_notes": sv.get("evaluated_notes", 0),
-                        "satisfied_count": sv.get("satisfied_count", 0),
-                        "average_confidence": sv.get("average_confidence", 0.0)
-                    },
-                    "search_result_file": sv["search_result_file"]
-                })
-
-        summary_data["candidates"].append(candidate_summary)
-
-    # 保存汇总文件
-    summary_file = os.path.join(search_results_dir, "summary.json")
-    with open(summary_file, 'w', encoding='utf-8') as f:
-        json.dump(summary_data, f, ensure_ascii=False, indent=2)
-    print(f"\n搜索结果汇总已保存: {summary_file}")
-
-    context.evaluation_results = results
-
-    # 构建详细的步骤记录数据
-    step_data = {
-        "candidate_count": len(candidates),
-        "candidates": candidates,
-        "total_evaluations": sum(len(r['evaluations']) for r in results),
-        "verified_queries": sum(
-            1 for r in results
-            for e in r.get('evaluations', [])
-            if 'search_verification' in e
-        ),
-        "search_results_dir": search_results_dir,
-        "summary_file": summary_file,
-        "detailed_results": []
-    }
-
-    # 为每个候选记录详细信息
-    for result in results:
-        candidate_detail = {
-            "candidate": result["candidate"],
-            "suggestions": result["suggestions"],
-            "evaluations": []
-        }
-
-        for eval_item in result.get("evaluations", []):
-            eval_detail = {
-                "query": eval_item["query"],
-                "intent_match": eval_item["intent_match"],
-                "relevance_score": eval_item["relevance_score"],
-                "reason": eval_item["reason"]
-            }
-
-            # 如果有搜索验证,添加详细信息
-            if "search_verification" in eval_item:
-                verification = eval_item["search_verification"]
-                eval_detail["search_verification"] = {
-                    "performed": True,
-                    "total_notes": verification.get("total_notes", 0),
-                    "evaluated_notes": verification.get("evaluated_notes", 0),
-                    "satisfied_count": verification.get("satisfied_count", 0),
-                    "average_confidence": verification.get("average_confidence", 0.0),
-                    "search_result_file": verification.get("search_result_file"),
-                    "has_error": "error" in verification
-                }
-                if "error" in verification:
-                    eval_detail["search_verification"]["error"] = verification["error"]
-
-                # 保存每个帖子的评估详情
-                if "note_evaluations" in verification:
-                    eval_detail["search_verification"]["note_evaluations"] = verification["note_evaluations"]
-            else:
-                eval_detail["search_verification"] = {
-                    "performed": False,
-                    "reason": "未达到搜索验证阈值(intent_match=False 或 relevance_score<0.7)"
-                }
-
-            candidate_detail["evaluations"].append(eval_detail)
-
-        step_data["detailed_results"].append(candidate_detail)
-
-    # 记录步骤
-    add_step(context, "评估候选query", "candidate_evaluation", step_data)
-
-    return results
-
-
-# ============================================================================
-# 新的独立步骤函数(方案A)
-# ============================================================================
-
-async def step_evaluate_query_suggestions(candidates: list[str], original_question: str, context: RunContext) -> list[dict]:
-    """
-    步骤1: 评估候选query的推荐词
-
-    输入:
-    - candidates: 候选query列表
-    - original_question: 原始问题
-    - context: 运行上下文
-
-    输出:
-    - 每个候选的评估结果列表,包含:
-      - candidate: 候选query
-      - suggestions: 推荐词列表
-      - evaluations: 每个推荐词的意图匹配和相关性评分
-    """
-    step_num = len(context.steps) + 1
-    print(f"\n{'='*60}")
-    print(f"[步骤 {step_num}] 评估 {len(candidates)} 个候选query的推荐词")
-    print(f"{'='*60}")
-
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-
-    async def evaluate_single_candidate(candidate: str):
-        print(f"\n评估候选:{candidate}")
-
-        # 1. 获取推荐词
-        suggestions = xiaohongshu_api.get_recommendations(keyword=candidate)
-        print(f"  获取到 {len(suggestions) if suggestions else 0} 个推荐词")
-
-        if not suggestions:
-            return {
-                "candidate": candidate,
-                "suggestions": [],
-                "evaluations": []
-            }
-
-        # 2. 评估每个推荐词(只做意图匹配和相关性评分)
-        async def eval_single_sug(sug: str):
-            eval_input = f"""
-<原始问题>
-{original_question}
-</原始问题>
-
-<待评估的推荐query>
-{sug}
-</待评估的推荐query>
-
-请评估该推荐query:
-1. intent_match: 意图是否匹配(true/false)
-2. relevance_score: 相关性分数(0-1)
-3. reason: 详细的评估理由
-"""
-            result = await Runner.run(evaluator, eval_input)
-            evaluation: RelevanceEvaluation = result.final_output
-
-            return {
-                "query": sug,
-                "intent_match": evaluation.intent_match,
-                "relevance_score": evaluation.relevance_score,
-                "reason": evaluation.reason
-            }
-
-        evaluations = await asyncio.gather(*[eval_single_sug(s) for s in suggestions])
-
-        return {
-            "candidate": candidate,
-            "suggestions": suggestions,
-            "evaluations": evaluations
-        }
-
-    results = await asyncio.gather(*[evaluate_single_candidate(c) for c in candidates])
-
-    # 记录步骤
-    add_step(context, "评估候选query的推荐词", "query_suggestion_evaluation", {
-        "candidate_count": len(candidates),
-        "candidates": candidates,
-        "results": results,
-        "total_evaluations": sum(len(r['evaluations']) for r in results),
-        "qualified_count": sum(
-            1 for r in results
-            for e in r['evaluations']
-            if e['intent_match'] and e['relevance_score'] >= 0.7
-        )
-    })
-
-    return results
-
-
-def step_filter_qualified_queries(evaluation_results: list[dict], context: RunContext, min_relevance_score: float = 0.7) -> list[dict]:
-    """
-    步骤1.5: 筛选合格的推荐词
-
-    输入:
-    - evaluation_results: 步骤1的评估结果
-
-    输出:
-    - 合格的query列表,每个包含:
-      - query: 推荐词
-      - from_candidate: 来源候选
-      - intent_match: 意图匹配
-      - relevance_score: 相关性分数
-      - reason: 评估理由
-    """
-    step_num = len(context.steps) + 1
-    print(f"\n{'='*60}")
-    print(f"[步骤 {step_num}] 筛选合格的推荐词")
-    print(f"{'='*60}")
-
-    qualified_queries = []
-    all_queries = []  # 保存所有查询,包括不合格的
-
-    for result in evaluation_results:
-        candidate = result["candidate"]
-        for eval_item in result.get("evaluations", []):
-            query_data = {
-                "query": eval_item["query"],
-                "from_candidate": candidate,
-                "intent_match": eval_item["intent_match"],
-                "relevance_score": eval_item["relevance_score"],
-                "reason": eval_item["reason"]
-            }
-
-            # 判断是否合格
-            is_qualified = (eval_item['intent_match'] is True
-                          and eval_item['relevance_score'] >= min_relevance_score)
-            query_data["is_qualified"] = is_qualified
-
-            all_queries.append(query_data)
-            if is_qualified:
-                qualified_queries.append(query_data)
-
-    # 按相关性分数降序排列
-    qualified_queries.sort(key=lambda x: x['relevance_score'], reverse=True)
-    all_queries.sort(key=lambda x: x['relevance_score'], reverse=True)
-
-    print(f"\n找到 {len(qualified_queries)} 个合格的推荐词 (共评估 {len(all_queries)} 个)")
-    if qualified_queries:
-        print(f"相关性分数范围: {qualified_queries[0]['relevance_score']:.2f} ~ {qualified_queries[-1]['relevance_score']:.2f}")
-        print("\n合格的推荐词:")
-        for idx, q in enumerate(qualified_queries[:5], 1):
-            print(f"  {idx}. {q['query']} (分数: {q['relevance_score']:.2f})")
-        if len(qualified_queries) > 5:
-            print(f"  ... 还有 {len(qualified_queries) - 5} 个")
-
-    # 记录步骤 - 保存所有查询数据
-    add_step(context, "筛选合格的推荐词", "filter_qualified_queries", {
-        "input_evaluation_count": len(all_queries),
-        "min_relevance_score": min_relevance_score,
-        "qualified_count": len(qualified_queries),
-        "qualified_queries": qualified_queries,
-        "all_queries": all_queries  # 新增:保存所有查询数据
-    })
-
-    return qualified_queries
-
-
-async def step_search_qualified_queries(qualified_queries: list[dict], context: RunContext) -> dict:
-    """
-    步骤2: 搜索合格的推荐词
-
-    输入:
-    - qualified_queries: 步骤1.5筛选出的合格query列表,每个包含:
-      - query: 推荐词
-      - from_candidate: 来源候选
-      - intent_match, relevance_score, reason
-
-    输出:
-    - 搜索结果字典,包含:
-      - searches: 每个query的搜索结果列表
-      - search_results_dir: 搜索结果保存目录
-    """
-    step_num = len(context.steps) + 1
-    print(f"\n{'='*60}")
-    print(f"[步骤 {step_num}] 搜索 {len(qualified_queries)} 个合格的推荐词")
-    print(f"{'='*60}")
-
-    if not qualified_queries:
-        add_step(context, "搜索合格的推荐词", "search_qualified_queries", {
-            "qualified_count": 0,
-            "searches": []
-        })
-        return {"searches": [], "search_results_dir": None}
-
-    # 创建搜索结果保存目录
-    search_results_dir = os.path.join(context.log_dir, "search_results")
-    os.makedirs(search_results_dir, exist_ok=True)
-
-    xiaohongshu_search = XiaohongshuSearch()
-
-    # 搜索每个合格的query
-    async def search_single_query(query_info: dict, query_index: int):
-        query = query_info['query']
-        print(f"\n搜索 [{query_index+1}/{len(qualified_queries)}]: {query}")
-
-        try:
-            # 执行搜索
-            search_result = xiaohongshu_search.search(keyword=query)
-
-            # 解析result字段
-            result_str = search_result.get("result", "{}")
-            if isinstance(result_str, str):
-                result_data = json.loads(result_str)
-            else:
-                result_data = result_str
-
-            # 格式化搜索结果
-            formatted_search_result = {
-                "success": search_result.get("success"),
-                "result": result_data,
-                "tool_name": search_result.get("tool_name"),
-                "call_type": search_result.get("call_type"),
-                "query": query,
-                "timestamp": datetime.now().isoformat()
-            }
-
-            # 保存到文件
-            safe_query_name = "".join(c if c.isalnum() or c in (' ', '_', '-') else '_' for c in query)
-            query_dir = os.path.join(search_results_dir, f"query_{query_index+1}_{safe_query_name[:50]}")
-            os.makedirs(query_dir, exist_ok=True)
-
-            search_result_file = os.path.join(query_dir, "search_result.json")
-            with open(search_result_file, 'w', encoding='utf-8') as f:
-                json.dump(formatted_search_result, f, ensure_ascii=False, indent=2)
-
-            # 提取帖子列表
-            notes = result_data.get("data", {}).get("data", [])
-
-            print(f"  → 搜索成功,获得 {len(notes)} 个帖子")
-
-            # ⭐ 提取帖子摘要信息用于steps.json
-            notes_summary = [process_note_data(note) for note in notes]
-
-            return {
-                "query": query,
-                "from_candidate": query_info['from_candidate'],
-                "intent_match": query_info['intent_match'],
-                "relevance_score": query_info['relevance_score'],
-                "reason": query_info['reason'],
-                "search_result_file": search_result_file,
-                "note_count": len(notes),
-                "notes": notes,  # 保存所有帖子用于评估
-                "notes_summary": notes_summary  # ⭐ 保存到steps.json
-            }
-
-        except Exception as e:
-            print(f"  → 搜索失败: {e}")
-            return {
-                "query": query,
-                "from_candidate": query_info['from_candidate'],
-                "intent_match": query_info['intent_match'],
-                "relevance_score": query_info['relevance_score'],
-                "reason": query_info['reason'],
-                "error": str(e),
-                "note_count": 0,
-                "notes": []
-            }
-
-    search_results = await asyncio.gather(*[search_single_query(q, i) for i, q in enumerate(qualified_queries)])
-
-    # 记录步骤
-    add_step(context, "搜索合格的推荐词", "search_qualified_queries", {
-        "qualified_count": len(qualified_queries),
-        "search_results": [
-            {
-                "query": sr['query'],
-                "from_candidate": sr['from_candidate'],
-                "note_count": sr['note_count'],
-                "search_result_file": sr.get('search_result_file'),
-                "has_error": 'error' in sr,
-                "notes_summary": sr.get('notes_summary', [])  # ⭐ 包含帖子摘要
-            }
-            for sr in search_results
-        ],
-        "search_results_dir": search_results_dir
-    })
-
-    return {
-        "searches": search_results,
-        "search_results_dir": search_results_dir
-    }
-
-
-async def step_evaluate_search_notes(search_data: dict, original_question: str, context: RunContext) -> dict:
-    """
-    步骤3: 评估搜索到的帖子
-
-    输入:
-    - search_data: 步骤2的搜索结果,包含:
-      - searches: 搜索结果列表
-      - search_results_dir: 结果目录
-
-    输出:
-    - 帖子评估结果字典,包含:
-      - note_evaluations: 每个query的帖子评估列表
-    """
-    step_num = len(context.steps) + 1
-    print(f"\n{'='*60}")
-    print(f"[步骤 {step_num}] 评估搜索到的帖子")
-    print(f"{'='*60}")
-
-    search_results = search_data['searches']
-
-    if not search_results:
-        add_step(context, "评估搜索到的帖子", "evaluate_search_notes", {
-            "query_count": 0,
-            "total_notes": 0,
-            "evaluated_notes": 0,
-            "note_evaluations": []
-        })
-        return {"note_evaluations": []}
-
-    # 对每个query的帖子进行评估
-    async def evaluate_query_notes(search_result: dict, query_index: int):
-        query = search_result['query']
-        notes = search_result.get('notes', [])
-
-        if not notes or 'error' in search_result:
-            return {
-                "query": query,
-                "from_candidate": search_result['from_candidate'],
-                "note_count": 0,
-                "evaluated_notes": [],
-                "satisfied_count": 0,
-                "average_confidence": 0.0
-            }
-
-        print(f"\n评估query [{query_index+1}]: {query} ({len(notes)} 个帖子)")
-
-        # 评估每个帖子
-        note_evaluations = []
-        for note_idx, note in enumerate(notes, 1):
-            # 使用标准化函数处理帖子数据
-            note_data = process_note_data(note)
-            title = note_data["title"]
-            desc = note_data["desc"]
-
-            # 调用评估Agent
-            eval_input = f"""
-<原始问题>
-{original_question}
-</原始问题>
-
-<帖子信息>
-标题: {title}
-描述: {desc}
-</帖子信息>
-
-请评估这个帖子能否满足用户需求。
-"""
-            eval_result_run = await Runner.run(note_evaluator, eval_input)
-            note_eval: NoteEvaluation = eval_result_run.final_output
-
-            # 合并标准化的帖子数据和评估结果
-            note_evaluations.append({
-                **note_data,  # 包含所有标准化字段
-                "note_index": note_idx,
-                "evaluation": {
-                    "title_relevance": note_eval.title_relevance,
-                    "content_expectation": note_eval.content_expectation,
-                    "need_satisfaction": note_eval.need_satisfaction,
-                    "confidence_score": note_eval.confidence_score,
-                    "reason": note_eval.reason
-                }
-            })
-
-            if note_idx % 3 == 0 or note_idx == len(notes):
-                print(f"  已评估 {note_idx}/{len(notes)} 个帖子")
-
-        # 统计
-        satisfied_count = sum(1 for ne in note_evaluations if ne["evaluation"]["need_satisfaction"])
-        avg_confidence = sum(ne["evaluation"]["confidence_score"] for ne in note_evaluations) / len(note_evaluations) if note_evaluations else 0
-
-        print(f"  → 完成:{satisfied_count}/{len(note_evaluations)} 个帖子满足需求")
-
-        return {
-            "query": query,
-            "from_candidate": search_result['from_candidate'],
-            "note_count": len(notes),
-            "evaluated_notes": note_evaluations,
-            "satisfied_count": satisfied_count,
-            "average_confidence": round(avg_confidence, 2)
-        }
-
-    # 并发评估所有query的帖子
-    all_evaluations = await asyncio.gather(*[evaluate_query_notes(sr, i) for i, sr in enumerate(search_results, 1)])
-
-    # 记录步骤
-    total_notes = sum(e['note_count'] for e in all_evaluations)
-    total_satisfied = sum(e['satisfied_count'] for e in all_evaluations)
-
-    add_step(context, "评估搜索到的帖子", "evaluate_search_notes", {
-        "query_count": len(search_results),
-        "total_notes": total_notes,
-        "total_satisfied": total_satisfied,
-        "note_evaluations": all_evaluations
-    })
-
-    return {"note_evaluations": all_evaluations}
-
-
-def step_collect_satisfied_notes(note_evaluation_data: dict) -> list[dict]:
-    """
-    步骤4: 汇总所有满足需求的帖子
-
-    输入:
-    - note_evaluation_data: 步骤3的帖子评估结果
-
-    输出:
-    - 所有满足需求的帖子列表,按置信度降序排列
-    """
-    print(f"\n{'='*60}")
-    print(f"汇总满足需求的帖子")
-    print(f"{'='*60}")
-
-    all_satisfied_notes = []
-
-    for query_eval in note_evaluation_data['note_evaluations']:
-        for note in query_eval['evaluated_notes']:
-            if note['evaluation']['need_satisfaction']:
-                all_satisfied_notes.append({
-                    "query": query_eval['query'],
-                    "from_candidate": query_eval['from_candidate'],
-                    "note_id": note['note_id'],
-                    "title": note['title'],
-                    "desc": note['desc'],
-                    # ⭐ 保留完整帖子信息
-                    "image_list": note.get('image_list', []),
-                    "cover_image": note.get('cover_image', {}),
-                    "interact_info": note.get('interact_info', {}),
-                    "user": note.get('user', {}),
-                    "type": note.get('type', 'normal'),
-                    "note_url": note.get('note_url', ''),
-                    # 评估结果
-                    "title_relevance": note['evaluation']['title_relevance'],
-                    "content_expectation": note['evaluation']['content_expectation'],
-                    "confidence_score": note['evaluation']['confidence_score'],
-                    "reason": note['evaluation']['reason']
-                })
-
-    # 按置信度降序排列
-    all_satisfied_notes.sort(key=lambda x: x['confidence_score'], reverse=True)
-
-    print(f"\n共收集到 {len(all_satisfied_notes)} 个满足需求的帖子")
-    if all_satisfied_notes:
-        print(f"置信度范围: {all_satisfied_notes[0]['confidence_score']:.2f} ~ {all_satisfied_notes[-1]['confidence_score']:.2f}")
-
-    return all_satisfied_notes
-
-
-async def step_generate_answer(satisfied_notes: list[dict], original_question: str, context: RunContext) -> dict:
-    """
-    步骤5: 基于满足需求的帖子生成答案
-
-    输入:
-    - satisfied_notes: 步骤4收集的满足需求的帖子列表
-    - original_question: 原始问题
-    - context: 运行上下文
-
-    输出:
-    - 生成的答案及相关信息
-      - answer: 答案内容(Markdown格式)
-      - cited_note_indices: 引用的帖子索引
-      - confidence: 答案置信度
-      - summary: 答案摘要
-      - cited_notes: 被引用的帖子详情
-    """
-    step_num = len(context.steps) + 1
-    print(f"\n{'='*60}")
-    print(f"[步骤 {step_num}] 基于 {len(satisfied_notes)} 个帖子生成答案")
-    print(f"{'='*60}")
-
-    if not satisfied_notes:
-        print("\n⚠️  没有满足需求的帖子,无法生成答案")
-        result = {
-            "answer": "抱歉,未找到能够回答该问题的相关内容。",
-            "cited_note_indices": [],
-            "confidence": 0.0,
-            "summary": "无可用信息",
-            "cited_notes": []
-        }
-
-        add_step(context, "生成答案", "answer_generation", {
-            "original_question": original_question,
-            "input_notes_count": 0,
-            "result": result
-        })
-
-        return result
-
-    # 构建Agent输入
-    notes_info = []
-    for idx, note in enumerate(satisfied_notes, 1):
-        notes_info.append(f"""
-【帖子 {idx}】
-标题: {note['title']}
-描述: {note['desc']}
-置信度: {note['confidence_score']:.2f}
-""".strip())
-
-    agent_input = f"""
-<原始问题>
-{original_question}
-</原始问题>
-
-<相关帖子>
-{chr(10).join(notes_info)}
-</相关帖子>
-
-请基于以上帖子,为原始问题生成一个全面、准确的答案。
-记得在答案中使用 [1], [2] 等标注引用的帖子序号。
-""".strip()
-
-    print(f"\n📝 调用答案生成Agent...")
-    print(f"  - 可用帖子: {len(satisfied_notes)} 个")
-    print(f"  - 平均置信度: {sum(n['confidence_score'] for n in satisfied_notes) / len(satisfied_notes):.2f}")
-
-    # 调用Agent生成答案
-    result_run = await Runner.run(answer_generator, agent_input)
-    answer_result: AnswerGeneration = result_run.final_output
-
-    # 提取被引用的帖子详情
-    cited_notes = []
-    for idx in answer_result.cited_note_indices:
-        if 1 <= idx <= len(satisfied_notes):
-            note = satisfied_notes[idx - 1]
-            cited_notes.append({
-                "index": idx,
-                "note_id": note['note_id'],
-                "title": note['title'],
-                "desc": note['desc'],
-                "confidence_score": note['confidence_score'],
-                # ⭐ 完整帖子信息用于可视化
-                "image_list": note.get('image_list', []),
-                "cover_image": note.get('cover_image', {}),
-                "interact_info": note.get('interact_info', {}),
-                "user": note.get('user', {}),
-                "note_url": note.get('note_url', ''),
-                "type": note.get('type', 'normal'),
-                # ⭐ 评估详情
-                "title_relevance": note.get('title_relevance', 0),
-                "content_expectation": note.get('content_expectation', 0),
-                "reason": note.get('reason', '')
-            })
-
-    result = {
-        "answer": answer_result.answer,
-        "cited_note_indices": answer_result.cited_note_indices,
-        "confidence": answer_result.confidence,
-        "summary": answer_result.summary,
-        "cited_notes": cited_notes
-    }
-
-    # 打印结果
-    print(f"\n✅ 答案生成完成")
-    print(f"  - 引用帖子数: {len(answer_result.cited_note_indices)} 个")
-    print(f"  - 答案置信度: {answer_result.confidence:.2f}")
-    print(f"  - 答案摘要: {answer_result.summary}")
-
-    # 记录步骤
-    add_step(context, "生成答案", "answer_generation", {
-        "original_question": original_question,
-        "input_notes_count": len(satisfied_notes),
-        "result": result,
-        "agent_input_preview": agent_input[:500] + "..." if len(agent_input) > 500 else agent_input
-    })
-
-    return result
-
-
-def find_qualified_queries(evaluation_results: list[dict], min_relevance_score: float = 0.7) -> list[dict]:
-    """
-    查找所有合格的query(旧函数,保留兼容性)
-
-    筛选标准:
-    1. intent_match = True(必须满足)
-    2. relevance_score >= min_relevance_score
-
-    返回:按 relevance_score 降序排列
-    """
-    all_qualified = []
-
-    for result in evaluation_results:
-        for eval_item in result.get("evaluations", []):
-            if (eval_item['intent_match'] is True
-                and eval_item['relevance_score'] >= min_relevance_score):
-                all_qualified.append({
-                    "from_candidate": result["candidate"],
-                    **eval_item
-                })
-
-    # 按relevance_score降序排列
-    return sorted(all_qualified, key=lambda x: x['relevance_score'], reverse=True)
-
-
-# ============================================================================
-# 主流程
-# ============================================================================
-
-async def progressive_exploration(context: RunContext, max_levels: int = 4) -> dict:
-    """
-    渐进式探索流程 - 使用独立步骤
-
-    流程:
-    1. 提取关键词 + 渐进式探索(复用旧流程)
-    2. 步骤1: 评估候选query的推荐词
-    3. 步骤2: 搜索合格的推荐词
-    4. 步骤3: 评估搜索到的帖子
-    5. 步骤4: 汇总满足需求的帖子
-    6. 步骤5: 生成答案
-
-    Args:
-        context: 运行上下文
-        max_levels: 最大探索层数,默认4
-
-    返回格式:
-    {
-        "success": True/False,
-        "final_answer": {...},  # 生成的答案
-        "satisfied_notes": [...],  # 满足需求的帖子
-        "message": "..."
-    }
-    """
-
-    # ========== 阶段1:渐进式探索(复用旧流程找到候选query)==========
-
-    # 1.1 提取关键词
-    keyword_result = await extract_keywords(context.q, context)
-    context.keywords = keyword_result.keywords
-
-    # 1.2 渐进式探索各层级
-    current_level = 1
-    candidates_to_evaluate = []
-
-    # Level 1:单个关键词
-    level_1_queries = context.keywords  # 使用所有关键词
-    level_1_data = await explore_level(level_1_queries, current_level, context)
-    analysis_1 = await analyze_level(level_1_data, context.exploration_levels, context.q, context)
-
-    if analysis_1.should_evaluate_now:
-        candidates_to_evaluate.extend(analysis_1.candidates_to_evaluate)
-
-    # Level 2及以后:迭代探索
-    for level_num in range(2, max_levels + 1):
-        prev_analysis: LevelAnalysis = context.level_analyses[-1]["analysis"]
-        prev_analysis = LevelAnalysis(**prev_analysis)
-
-        if not prev_analysis.next_combinations:
-            print(f"\nLevel {level_num-1} 分析后无需继续探索")
-            break
-
-        level_data = await explore_level(prev_analysis.next_combinations, level_num, context)
-        analysis = await analyze_level(level_data, context.exploration_levels, context.q, context)
-
-        if analysis.should_evaluate_now:
-            candidates_to_evaluate.extend(analysis.candidates_to_evaluate)
-
-    if not candidates_to_evaluate:
-        return {
-            "success": False,
-            "final_answer": None,
-            "satisfied_notes": [],
-            "message": "渐进式探索未找到候选query"
-        }
-
-    print(f"\n{'='*60}")
-    print(f"渐进式探索完成,找到 {len(candidates_to_evaluate)} 个候选query")
-    print(f"{'='*60}")
-
-    # ========== 阶段2:新的独立步骤流程 ==========
-
-    # 步骤1: 评估候选query的推荐词
-    evaluation_results = await step_evaluate_query_suggestions(
-        candidates_to_evaluate,
-        context.q,
-        context
-    )
-
-    # 步骤1.5: 筛选合格的推荐词
-    qualified_queries = step_filter_qualified_queries(
-        evaluation_results,
-        context,
-        min_relevance_score=0.7
-    )
-
-    if not qualified_queries:
-        return {
-            "success": False,
-            "final_answer": None,
-            "satisfied_notes": [],
-            "message": "没有合格的推荐词"
-        }
-
-    # 步骤2: 搜索合格的推荐词
-    search_results = await step_search_qualified_queries(
-        qualified_queries,
-        context
-    )
-
-    if not search_results.get('searches'):
-        return {
-            "success": False,
-            "final_answer": None,
-            "satisfied_notes": [],
-            "message": "搜索失败"
-        }
-
-    # 步骤3: 评估搜索到的帖子
-    note_evaluation_data = await step_evaluate_search_notes(
-        search_results,
-        context.q,
-        context
-    )
-
-    # 步骤4: 汇总满足需求的帖子
-    satisfied_notes = step_collect_satisfied_notes(note_evaluation_data)
-
-    if not satisfied_notes:
-        return {
-            "success": False,
-            "final_answer": None,
-            "satisfied_notes": [],
-            "message": "未找到满足需求的帖子"
-        }
-
-    # 步骤5: 生成答案
-    final_answer = await step_generate_answer(
-        satisfied_notes,
-        context.q,
-        context
-    )
-
-    # ========== 返回最终结果 ==========
-
-    return {
-        "success": True,
-        "final_answer": final_answer,
-        "satisfied_notes": satisfied_notes,
-        "message": f"成功找到 {len(satisfied_notes)} 个满足需求的帖子,并生成答案"
-    }
-
-
-# ============================================================================
-# 输出格式化
-# ============================================================================
-
-def format_output(optimization_result: dict, context: RunContext) -> str:
-    """
-    格式化输出结果 - 用于独立步骤流程
-
-    包含:
-    - 生成的答案
-    - 引用的帖子详情
-    - 满足需求的帖子统计
-    """
-    final_answer = optimization_result.get("final_answer")
-    satisfied_notes = optimization_result.get("satisfied_notes", [])
-
-    output = f"原始问题:{context.q}\n"
-    output += f"提取的关键词:{', '.join(context.keywords or [])}\n"
-    output += f"探索层数:{len(context.exploration_levels)}\n"
-    output += f"找到满足需求的帖子:{len(satisfied_notes)} 个\n"
-    output += "\n" + "="*60 + "\n"
-
-    if final_answer:
-        output += "【生成的答案】\n\n"
-        output += final_answer.get("answer", "")
-        output += "\n\n" + "="*60 + "\n"
-
-        output += f"答案置信度:{final_answer.get('confidence', 0):.2f}\n"
-        output += f"答案摘要:{final_answer.get('summary', '')}\n"
-        output += f"引用帖子数:{len(final_answer.get('cited_note_indices', []))} 个\n"
-        output += "\n" + "="*60 + "\n"
-
-        output += "【引用的帖子详情】\n\n"
-        for cited_note in final_answer.get("cited_notes", []):
-            output += f"[{cited_note['index']}] {cited_note['title']}\n"
-            output += f"    置信度: {cited_note['confidence_score']:.2f}\n"
-            output += f"    描述: {cited_note['desc']}\n"
-            output += f"    note_id: {cited_note['note_id']}\n\n"
-    else:
-        output += "未能生成答案\n"
-
-    return output
-
-
-# ============================================================================
-# 主函数
-# ============================================================================
-
-
-
-async def main(input_dir: str, max_levels: int = 4, visualize: bool = False):
-    """
-    主函数 - 使用独立步骤流程(方案A)
-    """
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]
-
-    # 日志保存目录
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-
-    # 执行渐进式探索
-    optimization_result = await progressive_exploration(run_context, max_levels=max_levels)
-
-    # 格式化输出
-    final_output = format_output(optimization_result, run_context)
-    print(f"\n{'='*60}")
-    print("最终结果")
-    print(f"{'='*60}")
-    print(final_output)
-
-    # 保存结果
-    run_context.optimization_result = optimization_result
-    run_context.final_output = final_output
-
-    # 记录最终输出步骤(新格式)
-    final_answer = optimization_result.get("final_answer")
-    satisfied_notes = optimization_result.get("satisfied_notes", [])
-
-    add_step(run_context, "生成最终结果", "final_result", {
-        "success": optimization_result["success"],
-        "message": optimization_result["message"],
-        "satisfied_notes_count": len(satisfied_notes),
-        "final_answer": final_answer,
-        "satisfied_notes_summary": [
-            {
-                "note_id": note["note_id"],
-                "title": note["title"],
-                "confidence_score": note["confidence_score"]
-            }
-            for note in satisfied_notes  # 保存所有满足条件的帖子摘要
-        ] if satisfied_notes else [],
-        "final_output": final_output
-    })
-
-    # 保存 RunContext 到 log_dir(不包含 steps,steps 单独保存)
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    context_dict = run_context.model_dump()
-    context_dict.pop('steps', None)  # 移除 steps,避免数据冗余
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(context_dict, f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-    # 保存步骤化日志
-    steps_file_path = os.path.join(run_context.log_dir, "steps.json")
-    with open(steps_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.steps, f, ensure_ascii=False, indent=2)
-    print(f"Steps log saved to: {steps_file_path}")
-
-    # 如果需要生成可视化
-    if visualize:
-        import subprocess
-        output_html = os.path.join(run_context.log_dir, "visualization.html")
-        print(f"\n🎨 生成可视化HTML...")
-        result = subprocess.run([
-            "python", "sug_v6_1_2_3.visualize.py",
-            steps_file_path,
-            "-o", output_html
-        ])
-        if result.returncode == 0:
-            print(f"✅ 可视化已生成: {output_html}")
-        else:
-            print(f"❌ 可视化生成失败")
-
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具 - v6.1.2.3 独立步骤+答案生成版")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    parser.add_argument(
-        "--max-levels",
-        type=int,
-        default=10,
-        help="最大探索层数,默认: 10"
-    )
-    parser.add_argument(
-        "--visualize",
-        action="store_true",
-        default=True,
-        help="运行完成后自动生成可视化HTML(默认开启)"
-    )
-    parser.add_argument(
-        "--no-visualize",
-        action="store_false",
-        dest="visualize",
-        help="关闭自动生成可视化"
-    )
-    parser.add_argument(
-        "--visualize-only",
-        action="store_true",
-        help="只生成可视化,不运行搜索流程。自动查找input-dir下最新的输出目录"
-    )
-    args = parser.parse_args()
-
-    # 如果只是生成可视化
-    if args.visualize_only:
-        import subprocess
-        import glob
-
-        # 获取版本名称
-        version_name = os.path.splitext(os.path.basename(__file__))[0]
-        output_base = os.path.join(args.input_dir, "output", version_name)
-
-        # 查找最新的输出目录
-        if not os.path.exists(output_base):
-            print(f"❌ 找不到输出目录: {output_base}")
-            sys.exit(1)
-
-        # 获取所有日期目录
-        date_dirs = glob.glob(os.path.join(output_base, "*", "*"))
-        if not date_dirs:
-            print(f"❌ 在 {output_base} 中没有找到输出目录")
-            sys.exit(1)
-
-        # 按修改时间排序,获取最新的
-        latest_dir = max(date_dirs, key=os.path.getmtime)
-        steps_json = os.path.join(latest_dir, "steps.json")
-
-        if not os.path.exists(steps_json):
-            print(f"❌ 找不到 steps.json: {steps_json}")
-            sys.exit(1)
-
-        output_html = os.path.join(latest_dir, "visualization.html")
-        print(f"🎨 找到最新输出目录: {latest_dir}")
-        print(f"🎨 生成可视化: {steps_json} -> {output_html}")
-
-        result = subprocess.run([
-            "python", "sug_v6_1_2_3.visualize.py",
-            steps_json,
-            "-o", output_html
-        ])
-        sys.exit(result.returncode)
-
-    asyncio.run(main(args.input_dir, max_levels=args.max_levels, visualize=args.visualize))

+ 0 - 1551
sug_v6_1_2_5.py

@@ -1,1551 +0,0 @@
-import asyncio
-import json
-import os
-import sys
-import argparse
-from datetime import datetime
-from typing import Literal
-
-from agents import Agent, Runner
-from lib.my_trace import set_trace
-from pydantic import BaseModel, Field
-
-from lib.utils import read_file_as_string
-from lib.client import get_model
-MODEL_NAME = "google/gemini-2.5-flash"
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-from script.search.xiaohongshu_search import XiaohongshuSearch
-
-
-# ============================================================================
-# 数据模型
-# ============================================================================
-
-class QueryState(BaseModel):
-    """Query状态跟踪"""
-    query: str
-    level: int  # 当前所在层级
-    no_suggestion_rounds: int = 0  # 连续没有suggestion的轮数
-    relevance_score: float = 0.0  # 与原始需求的相关度
-    parent_query: str | None = None  # 父query
-    strategy: str | None = None  # 生成策略:direct_sug, rewrite, add_word
-    is_terminated: bool = False  # 是否已终止(不再处理)
-
-
-class WordLibrary(BaseModel):
-    """动态分词库"""
-    words: set[str] = Field(default_factory=set)
-    word_sources: dict[str, str] = Field(default_factory=dict)  # 记录词的来源:word -> source(note_id或"initial")
-    core_words: set[str] = Field(default_factory=set)  # 核心词(第一层初始分词)
-
-    def add_word(self, word: str, source: str = "unknown", is_core: bool = False):
-        """添加单词到分词库"""
-        if word and word.strip():
-            word = word.strip()
-            self.words.add(word)
-            if word not in self.word_sources:
-                self.word_sources[word] = source
-            if is_core:
-                self.core_words.add(word)
-
-    def add_words(self, words: list[str], source: str = "unknown", is_core: bool = False):
-        """批量添加单词"""
-        for word in words:
-            self.add_word(word, source, is_core)
-
-    def get_unused_word(self, current_query: str, prefer_core: bool = True) -> str | None:
-        """获取一个当前query中没有的词
-
-        Args:
-            current_query: 当前查询
-            prefer_core: 是否优先返回核心词(默认True)
-        """
-        # 优先从核心词中查找
-        if prefer_core and self.core_words:
-            for word in self.core_words:
-                if word not in current_query:
-                    return word
-
-        # 如果核心词都用完了,或者不优先使用核心词,从所有词中查找
-        for word in self.words:
-            if word not in current_query:
-                return word
-        return None
-
-    def model_dump(self):
-        """序列化为dict"""
-        return {
-            "words": list(self.words),
-            "word_sources": self.word_sources,
-            "core_words": list(self.core_words)
-        }
-
-
-class RunContext(BaseModel):
-    """运行上下文"""
-    version: str
-    input_files: dict[str, str]
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-
-    # 新增字段
-    word_library: dict = Field(default_factory=dict)  # 使用dict存储,因为set不能直接序列化
-    query_states: list[dict] = Field(default_factory=list)
-    steps: list[dict] = Field(default_factory=list)
-
-    # Query演化图
-    query_graph: dict = Field(default_factory=dict)  # 记录Query的演化路径和关系
-
-    # 最终结果
-    satisfied_notes: list[dict] = Field(default_factory=list)
-    final_output: str | None = None
-
-
-# ============================================================================
-# Agent 定义
-# ============================================================================
-
-# Agent 1: 分词专家
-class WordSegmentation(BaseModel):
-    """分词结果"""
-    words: list[str] = Field(..., description="分词结果列表")
-    reasoning: str = Field(..., description="分词理由")
-
-word_segmentation_instructions = """
-你是分词专家。给定一个query,将其拆分成有意义的最小单元。
-
-## 分词原则
-1. 保留有搜索意义的词汇
-2. 拆分成独立的概念
-3. 保留专业术语的完整性
-4. 去除虚词(的、吗、呢等)
-
-## 输出要求
-返回分词列表和分词理由。
-""".strip()
-
-word_segmenter = Agent[None](
-    name="分词专家",
-    instructions=word_segmentation_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=WordSegmentation,
-)
-
-
-# Agent 2: Query相关度评估专家
-class RelevanceEvaluation(BaseModel):
-    """相关度评估"""
-    relevance_score: float = Field(..., description="相关性分数 0-1")
-    is_improved: bool = Field(..., description="是否比之前更好")
-    reason: str = Field(..., description="评估理由")
-
-relevance_evaluation_instructions = """
-你是Query相关度评估专家。
-
-## 任务
-评估当前query与原始需求的匹配程度。
-
-## 评估标准
-- 主题相关性
-- 要素覆盖度
-- 意图匹配度
-
-## 输出
-- relevance_score: 0-1的相关性分数
-- is_improved: 如果提供了previous_score,判断是否有提升
-- reason: 详细理由
-""".strip()
-
-relevance_evaluator = Agent[None](
-    name="Query相关度评估专家",
-    instructions=relevance_evaluation_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=RelevanceEvaluation,
-)
-
-
-# Agent 3: Query改写专家
-class QueryRewrite(BaseModel):
-    """Query改写结果"""
-    rewritten_query: str = Field(..., description="改写后的query")
-    rewrite_type: str = Field(..., description="改写类型:abstract或synonym")
-    reasoning: str = Field(..., description="改写理由")
-
-query_rewrite_instructions = """
-你是Query改写专家。
-
-## 改写策略
-1. **向上抽象**:将具体概念泛化到更高层次
-   - 例:iPhone 13 → 智能手机
-2. **同义改写**:使用同义词或相关表达
-   - 例:购买 → 入手、获取
-
-## 输出要求
-返回改写后的query、改写类型和理由。
-""".strip()
-
-query_rewriter = Agent[None](
-    name="Query改写专家",
-    instructions=query_rewrite_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=QueryRewrite,
-)
-
-
-# Agent 4: 加词位置评估专家
-class WordInsertion(BaseModel):
-    """加词结果"""
-    new_query: str = Field(..., description="加词后的新query")
-    insertion_position: str = Field(..., description="插入位置描述")
-    reasoning: str = Field(..., description="插入理由")
-
-word_insertion_instructions = """
-你是加词位置评估专家。
-
-## 任务
-将新词加到当前query的最合适位置,保持语义通顺。
-
-## 原则
-1. 保持语法正确
-2. 语义连贯
-3. 符合搜索习惯
-
-## 输出
-返回新query、插入位置描述和理由。
-""".strip()
-
-word_inserter = Agent[None](
-    name="加词位置评估专家",
-    instructions=word_insertion_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=WordInsertion,
-)
-
-
-# Agent 5: Result匹配度评估专家
-class ResultEvaluation(BaseModel):
-    """Result评估结果"""
-    match_level: str = Field(..., description="匹配等级:satisfied, partial, unsatisfied")
-    relevance_score: float = Field(..., description="相关性分数 0-1")
-    missing_aspects: list[str] = Field(default_factory=list, description="缺失的方面")
-    reason: str = Field(..., description="评估理由")
-
-result_evaluation_instructions = """
-你是Result匹配度评估专家。
-
-## 任务
-评估搜索结果(帖子)与原始需求的匹配程度。
-
-## 评估等级
-1. **satisfied**: 完全满足需求
-2. **partial**: 部分满足,但有缺失
-3. **unsatisfied**: 基本不满足
-
-## 输出要求
-- match_level: 匹配等级
-- relevance_score: 相关性分数
-- missing_aspects: 如果是partial,列出缺失的方面
-- reason: 详细理由
-""".strip()
-
-result_evaluator = Agent[None](
-    name="Result匹配度评估专家",
-    instructions=result_evaluation_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=ResultEvaluation,
-)
-
-
-# Agent 6: Query改造专家(基于缺失部分)
-class QueryImprovement(BaseModel):
-    """Query改造结果"""
-    improved_query: str = Field(..., description="改造后的query")
-    added_aspects: list[str] = Field(..., description="添加的方面")
-    reasoning: str = Field(..., description="改造理由")
-
-query_improvement_instructions = """
-你是Query改造专家。
-
-## 任务
-根据搜索结果的缺失部分,改造query使其包含这些内容。
-
-## 原则
-1. 针对性补充缺失方面
-2. 保持query简洁
-3. 符合搜索习惯
-
-## 输出
-返回改造后的query、添加的方面和理由。
-""".strip()
-
-query_improver = Agent[None](
-    name="Query改造专家",
-    instructions=query_improvement_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=QueryImprovement,
-)
-
-
-# Agent 7: 关键词提取专家
-class KeywordExtraction(BaseModel):
-    """关键词提取结果"""
-    keywords: list[str] = Field(..., description="提取的关键词列表")
-    reasoning: str = Field(..., description="提取理由")
-
-keyword_extraction_instructions = """
-你是关键词提取专家。
-
-## 任务
-从帖子标题和描述中提取核心关键词。
-
-## 提取原则
-1. 提取有搜索价值的词汇
-2. 去除虚词和通用词
-3. 保留专业术语
-4. 提取3-10个关键词
-
-## 输出
-返回关键词列表和提取理由。
-""".strip()
-
-keyword_extractor = Agent[None](
-    name="关键词提取专家",
-    instructions=keyword_extraction_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=KeywordExtraction,
-)
-
-
-# ============================================================================
-# 辅助函数
-# ============================================================================
-
-def add_step(context: RunContext, step_name: str, step_type: str, data: dict):
-    """添加步骤记录"""
-    step = {
-        "step_number": len(context.steps) + 1,
-        "step_name": step_name,
-        "step_type": step_type,
-        "timestamp": datetime.now().isoformat(),
-        "data": data
-    }
-    context.steps.append(step)
-    return step
-
-
-def add_query_to_graph(context: RunContext, query_state: QueryState, iteration: int, evaluation_reason: str = "", is_selected: bool = True, parent_level: int | None = None):
-    """添加Query节点到演化图
-
-    Args:
-        context: 运行上下文
-        query_state: Query状态
-        iteration: 迭代次数
-        evaluation_reason: 评估原因(可选)
-        is_selected: 是否被选中进入处理队列(默认True)
-        parent_level: 父节点的层级(用于构造parent_id)
-    """
-    # 使用 "query_level" 格式作为节点ID
-    query_id = f"{query_state.query}_{query_state.level}"
-
-    # 初始化图结构
-    if "nodes" not in context.query_graph:
-        context.query_graph["nodes"] = {}
-        context.query_graph["edges"] = []
-        context.query_graph["iterations"] = {}
-
-    # 添加Query节点(type: query)
-    context.query_graph["nodes"][query_id] = {
-        "type": "query",
-        "query": query_state.query,
-        "level": query_state.level,
-        "relevance_score": query_state.relevance_score,
-        "strategy": query_state.strategy,
-        "parent_query": query_state.parent_query,
-        "iteration": iteration,
-        "is_terminated": query_state.is_terminated,
-        "no_suggestion_rounds": query_state.no_suggestion_rounds,
-        "evaluation_reason": evaluation_reason,  # 评估原因
-        "is_selected": is_selected  # 是否被选中
-    }
-
-    # 添加边(父子关系)
-    if query_state.parent_query and parent_level is not None:
-        # 构造父节点ID: parent_query_parent_level
-        parent_id = f"{query_state.parent_query}_{parent_level}"
-        if parent_id in context.query_graph["nodes"]:
-            context.query_graph["edges"].append({
-                "from": parent_id,
-                "to": query_id,
-                "edge_type": "query_to_query",
-                "strategy": query_state.strategy,
-                "score_improvement": query_state.relevance_score - context.query_graph["nodes"][parent_id]["relevance_score"]
-            })
-
-    # 按迭代分组
-    if iteration not in context.query_graph["iterations"]:
-        context.query_graph["iterations"][iteration] = []
-    context.query_graph["iterations"][iteration].append(query_id)
-
-
-def add_note_to_graph(context: RunContext, query: str, query_level: int, note: dict):
-    """添加Note节点到演化图,并连接到对应的Query
-
-    Args:
-        context: 运行上下文
-        query: query文本
-        query_level: query所在层级
-        note: 帖子数据
-    """
-    note_id = note["note_id"]
-
-    # 初始化图结构
-    if "nodes" not in context.query_graph:
-        context.query_graph["nodes"] = {}
-        context.query_graph["edges"] = []
-        context.query_graph["iterations"] = {}
-
-    # 添加Note节点(type: note),包含完整的元信息
-    context.query_graph["nodes"][note_id] = {
-        "type": "note",
-        "note_id": note_id,
-        "title": note["title"],
-        "desc": note.get("desc", ""),  # 完整描述,不截断
-        "note_url": note.get("note_url", ""),
-        "image_list": note.get("image_list", []),  # 图片列表
-        "interact_info": note.get("interact_info", {}),  # 互动信息(点赞、收藏、评论、分享)
-        "match_level": note["evaluation"]["match_level"],
-        "relevance_score": note["evaluation"]["relevance_score"],
-        "evaluation_reason": note["evaluation"].get("reason", ""),  # 评估原因
-        "found_by_query": query
-    }
-
-    # 添加边:Query → Note,使用 query_level 格式的ID
-    query_id = f"{query}_{query_level}"
-    if query_id in context.query_graph["nodes"]:
-        context.query_graph["edges"].append({
-            "from": query_id,
-            "to": note_id,
-            "edge_type": "query_to_note",
-            "match_level": note["evaluation"]["match_level"],
-            "relevance_score": note["evaluation"]["relevance_score"]
-        })
-
-
-def process_note_data(note: dict) -> dict:
-    """处理搜索接口返回的帖子数据"""
-    note_card = note.get("note_card", {})
-    image_list = note_card.get("image_list", [])
-    interact_info = note_card.get("interact_info", {})
-    user_info = note_card.get("user", {})
-
-    return {
-        "note_id": note.get("id", ""),
-        "title": note_card.get("display_title", ""),
-        "desc": note_card.get("desc", ""),
-        "image_list": image_list,
-        "interact_info": {
-            "liked_count": interact_info.get("liked_count", 0),
-            "collected_count": interact_info.get("collected_count", 0),
-            "comment_count": interact_info.get("comment_count", 0),
-            "shared_count": interact_info.get("shared_count", 0)
-        },
-        "user": {
-            "nickname": user_info.get("nickname", ""),
-            "user_id": user_info.get("user_id", "")
-        },
-        "type": note_card.get("type", "normal"),
-        "note_url": f"https://www.xiaohongshu.com/explore/{note.get('id', '')}"
-    }
-
-
-# ============================================================================
-# 核心流程函数
-# ============================================================================
-
-async def initialize_word_library(original_query: str, context: RunContext) -> WordLibrary:
-    """初始化分词库"""
-    print("\n[初始化] 创建分词库...")
-
-    # 使用Agent进行分词
-    result = await Runner.run(word_segmenter, original_query)
-    segmentation: WordSegmentation = result.final_output
-
-    word_lib = WordLibrary()
-    # 初始分词标记为核心词(is_core=True)
-    word_lib.add_words(segmentation.words, source="initial", is_core=True)
-
-    print(f"初始分词库(核心词): {list(word_lib.words)}")
-    print(f"分词理由: {segmentation.reasoning}")
-
-    # 保存到context
-    context.word_library = word_lib.model_dump()
-
-    add_step(context, "初始化分词库", "word_library_init", {
-        "agent": "分词专家",
-        "input": original_query,
-        "output": {
-            "words": segmentation.words,
-            "reasoning": segmentation.reasoning
-        },
-        "result": {
-            "word_library": list(word_lib.words)
-        }
-    })
-
-    return word_lib
-
-
-async def evaluate_query_relevance(
-    query: str,
-    original_need: str,
-    previous_score: float | None = None,
-    context: RunContext = None
-) -> RelevanceEvaluation:
-    """评估query与原始需求的相关度"""
-
-    eval_input = f"""
-<原始需求>
-{original_need}
-</原始需求>
-
-<当前Query>
-{query}
-</当前Query>
-
-{"<之前的相关度分数>" + str(previous_score) + "</之前的相关度分数>" if previous_score is not None else ""}
-
-请评估当前query与原始需求的相关度。
-"""
-
-    result = await Runner.run(relevance_evaluator, eval_input)
-    evaluation: RelevanceEvaluation = result.final_output
-
-    return evaluation
-
-
-async def process_suggestions(
-    query: str,
-    query_state: QueryState,
-    original_need: str,
-    word_lib: WordLibrary,
-    context: RunContext,
-    xiaohongshu_api: XiaohongshuSearchRecommendations,
-    iteration: int
-) -> list[QueryState]:
-    """处理suggestion分支,返回新的query states"""
-
-    print(f"\n  [Suggestion分支] 处理query: {query}")
-
-    # 收集本次分支处理中的所有Agent调用
-    agent_calls = []
-
-    # 1. 获取suggestions
-    suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-
-    if not suggestions or len(suggestions) == 0:
-        print(f"    → 没有获取到suggestion")
-        query_state.no_suggestion_rounds += 1
-
-        # 记录步骤
-        add_step(context, f"Suggestion分支 - {query}", "suggestion_branch", {
-            "query": query,
-            "query_level": query_state.level,
-            "suggestions_count": 0,
-            "no_suggestion_rounds": query_state.no_suggestion_rounds,
-            "new_queries_generated": 0
-        })
-
-        return []
-
-    print(f"    → 获取到 {len(suggestions)} 个suggestions")
-    query_state.no_suggestion_rounds = 0  # 重置计数
-
-    # 2. 评估每个suggestion
-    new_queries = []
-    suggestion_evaluations = []
-
-    for sug in suggestions:  # 处理所有建议
-        # 评估sug与原始需求的相关度(注意:这里是与原始需求original_need对比,而非当前query)
-        # 这样可以确保生成的suggestion始终围绕用户的核心需求
-        sug_eval = await evaluate_query_relevance(sug, original_need, query_state.relevance_score, context)
-
-        sug_eval_record = {
-            "suggestion": sug,
-            "relevance_score": sug_eval.relevance_score,
-            "is_improved": sug_eval.is_improved,
-            "reason": sug_eval.reason
-        }
-        suggestion_evaluations.append(sug_eval_record)
-
-        # 创建query state(所有suggestion都作为query节点)
-        sug_state = QueryState(
-            query=sug,
-            level=query_state.level + 1,
-            relevance_score=sug_eval.relevance_score,
-            parent_query=query,
-            strategy="调用sug"
-        )
-
-        # 判断是否比当前query更好(只有提升的才加入待处理队列)
-        is_selected = sug_eval.is_improved and sug_eval.relevance_score > query_state.relevance_score
-
-        # 将所有suggestion添加到演化图(包括未提升的)
-        add_query_to_graph(
-            context,
-            sug_state,
-            iteration,
-            evaluation_reason=sug_eval.reason,
-            is_selected=is_selected,
-            parent_level=query_state.level  # 父节点的层级
-        )
-
-        if is_selected:
-            print(f"      ✓ {sug} (分数: {sug_eval.relevance_score:.2f}, 提升: {sug_eval.is_improved})")
-            new_queries.append(sug_state)
-        else:
-            print(f"      ✗ {sug} (分数: {sug_eval.relevance_score:.2f}, 未提升)")
-
-    # 3. 改写策略(向上抽象或同义改写)
-    if len(new_queries) < 3:  # 如果直接使用sug的数量不够,尝试改写
-        # 尝试向上抽象
-        rewrite_input_abstract = f"""
-<当前Query>
-{query}
-</当前Query>
-
-<改写要求>
-类型: abstract (向上抽象)
-</改写要求>
-
-请改写这个query。
-"""
-        result = await Runner.run(query_rewriter, rewrite_input_abstract)
-        rewrite: QueryRewrite = result.final_output
-
-        # 收集改写Agent的输入输出
-        rewrite_agent_call = {
-            "agent": "Query改写专家",
-            "action": "向上抽象改写",
-            "input": {
-                "query": query,
-                "rewrite_type": "abstract"
-            },
-            "output": {
-                "rewritten_query": rewrite.rewritten_query,
-                "rewrite_type": rewrite.rewrite_type,
-                "reasoning": rewrite.reasoning
-            }
-        }
-        agent_calls.append(rewrite_agent_call)
-
-        # 评估改写后的query
-        rewrite_eval = await evaluate_query_relevance(rewrite.rewritten_query, original_need, query_state.relevance_score, context)
-
-        # 创建改写后的query state
-        new_state = QueryState(
-            query=rewrite.rewritten_query,
-            level=query_state.level + 1,
-            relevance_score=rewrite_eval.relevance_score,
-            parent_query=query,
-            strategy="抽象改写"
-        )
-
-        # 添加到演化图(无论是否提升)
-        add_query_to_graph(
-            context,
-            new_state,
-            iteration,
-            evaluation_reason=rewrite_eval.reason,
-            is_selected=rewrite_eval.is_improved,
-            parent_level=query_state.level  # 父节点的层级
-        )
-
-        if rewrite_eval.is_improved:
-            print(f"      ✓ 改写(抽象): {rewrite.rewritten_query} (分数: {rewrite_eval.relevance_score:.2f})")
-            new_queries.append(new_state)
-        else:
-            print(f"      ✗ 改写(抽象): {rewrite.rewritten_query} (分数: {rewrite_eval.relevance_score:.2f}, 未提升)")
-
-    # 3.2. 同义改写策略
-    if len(new_queries) < 4:  # 如果还不够,尝试同义改写
-        rewrite_input_synonym = f"""
-<当前Query>
-{query}
-</当前Query>
-
-<改写要求>
-类型: synonym (同义改写)
-使用同义词或相关表达来改写query,保持语义相同但表达方式不同。
-</改写要求>
-
-请改写这个query。
-"""
-        result = await Runner.run(query_rewriter, rewrite_input_synonym)
-        rewrite_syn: QueryRewrite = result.final_output
-
-        # 收集同义改写Agent的输入输出
-        rewrite_syn_agent_call = {
-            "agent": "Query改写专家",
-            "action": "同义改写",
-            "input": {
-                "query": query,
-                "rewrite_type": "synonym"
-            },
-            "output": {
-                "rewritten_query": rewrite_syn.rewritten_query,
-                "rewrite_type": rewrite_syn.rewrite_type,
-                "reasoning": rewrite_syn.reasoning
-            }
-        }
-        agent_calls.append(rewrite_syn_agent_call)
-
-        # 评估改写后的query
-        rewrite_syn_eval = await evaluate_query_relevance(rewrite_syn.rewritten_query, original_need, query_state.relevance_score, context)
-
-        # 创建改写后的query state
-        new_state = QueryState(
-            query=rewrite_syn.rewritten_query,
-            level=query_state.level + 1,
-            relevance_score=rewrite_syn_eval.relevance_score,
-            parent_query=query,
-            strategy="同义改写"
-        )
-
-        # 添加到演化图(无论是否提升)
-        add_query_to_graph(
-            context,
-            new_state,
-            iteration,
-            evaluation_reason=rewrite_syn_eval.reason,
-            is_selected=rewrite_syn_eval.is_improved,
-            parent_level=query_state.level  # 父节点的层级
-        )
-
-        if rewrite_syn_eval.is_improved:
-            print(f"      ✓ 改写(同义): {rewrite_syn.rewritten_query} (分数: {rewrite_syn_eval.relevance_score:.2f})")
-            new_queries.append(new_state)
-        else:
-            print(f"      ✗ 改写(同义): {rewrite_syn.rewritten_query} (分数: {rewrite_syn_eval.relevance_score:.2f}, 未提升)")
-
-    # 4. 加词策略(优先使用核心词)
-    unused_word = word_lib.get_unused_word(query, prefer_core=True)
-    is_core_word = unused_word in word_lib.core_words if unused_word else False
-
-    if unused_word and len(new_queries) < 5:
-        word_type = "核心词" if is_core_word else "普通词"
-        insertion_input = f"""
-<当前Query>
-{query}
-</当前Query>
-
-<要添加的词>
-{unused_word}
-</要添加的词>
-
-请将这个词加到query的最合适位置。
-"""
-        result = await Runner.run(word_inserter, insertion_input)
-        insertion: WordInsertion = result.final_output
-
-        # 收集加词Agent的输入输出
-        insertion_agent_call = {
-            "agent": "加词位置评估专家",
-            "action": f"加词({word_type})",
-            "input": {
-                "query": query,
-                "word_to_add": unused_word,
-                "is_core_word": is_core_word
-            },
-            "output": {
-                "new_query": insertion.new_query,
-                "insertion_position": insertion.insertion_position,
-                "reasoning": insertion.reasoning
-            }
-        }
-        agent_calls.append(insertion_agent_call)
-
-        # 评估加词后的query
-        insertion_eval = await evaluate_query_relevance(insertion.new_query, original_need, query_state.relevance_score, context)
-
-        # 创建加词后的query state
-        new_state = QueryState(
-            query=insertion.new_query,
-            level=query_state.level + 1,
-            relevance_score=insertion_eval.relevance_score,
-            parent_query=query,
-            strategy="加词"
-        )
-
-        # 添加到演化图(无论是否提升)
-        add_query_to_graph(
-            context,
-            new_state,
-            iteration,
-            evaluation_reason=insertion_eval.reason,
-            is_selected=insertion_eval.is_improved,
-            parent_level=query_state.level  # 父节点的层级
-        )
-
-        if insertion_eval.is_improved:
-            print(f"      ✓ 加词({word_type}): {insertion.new_query} [+{unused_word}] (分数: {insertion_eval.relevance_score:.2f})")
-            new_queries.append(new_state)
-        else:
-            print(f"      ✗ 加词({word_type}): {insertion.new_query} [+{unused_word}] (分数: {insertion_eval.relevance_score:.2f}, 未提升)")
-
-    # 记录完整的suggestion分支处理结果(层级化)
-    add_step(context, f"Suggestion分支 - {query}", "suggestion_branch", {
-        "query": query,
-        "query_level": query_state.level,
-        "query_relevance": query_state.relevance_score,
-        "suggestions_count": len(suggestions),
-        "suggestions_evaluated": len(suggestion_evaluations),
-        "suggestion_evaluations": suggestion_evaluations,  # 保存所有评估
-        "agent_calls": agent_calls,  # 所有Agent调用的详细记录
-        "new_queries_generated": len(new_queries),
-        "new_queries": [{"query": nq.query, "score": nq.relevance_score, "strategy": nq.strategy} for nq in new_queries],
-        "no_suggestion_rounds": query_state.no_suggestion_rounds
-    })
-
-    return new_queries
-
-
-async def process_search_results(
-    query: str,
-    query_state: QueryState,
-    original_need: str,
-    word_lib: WordLibrary,
-    context: RunContext,
-    xiaohongshu_search: XiaohongshuSearch,
-    relevance_threshold: float,
-    iteration: int
-) -> tuple[list[dict], list[QueryState]]:
-    """
-    处理搜索结果分支
-    返回: (满足需求的notes, 需要继续迭代的新queries)
-    """
-
-    print(f"\n  [Result分支] 搜索query: {query}")
-
-    # 收集本次分支处理中的所有Agent调用
-    agent_calls = []
-
-    # 1. 判断query相关度是否达到门槛
-    if query_state.relevance_score < relevance_threshold:
-        print(f"    ✗ 相关度 {query_state.relevance_score:.2f} 低于门槛 {relevance_threshold},跳过搜索")
-        return [], []
-
-    print(f"    ✓ 相关度 {query_state.relevance_score:.2f} 达到门槛,执行搜索")
-
-    # 2. 执行搜索
-    try:
-        search_result = xiaohongshu_search.search(keyword=query)
-        result_str = search_result.get("result", "{}")
-        if isinstance(result_str, str):
-            result_data = json.loads(result_str)
-        else:
-            result_data = result_str
-
-        notes = result_data.get("data", {}).get("data", [])
-        print(f"    → 搜索到 {len(notes)} 个帖子")
-
-    except Exception as e:
-        print(f"    ✗ 搜索失败: {e}")
-        return [], []
-
-    if not notes:
-        return [], []
-
-    # 3. 评估每个帖子
-    satisfied_notes = []
-    partial_notes = []
-
-    for note in notes:  # 评估所有帖子
-        note_data = process_note_data(note)
-        title = note_data["title"] or ""
-        desc = note_data["desc"] or ""
-
-        # 跳过空标题和描述的帖子
-        if not title and not desc:
-            continue
-
-        # 评估帖子
-        eval_input = f"""
-<原始需求>
-{original_need}
-</原始需求>
-
-<帖子>
-标题: {title}
-描述: {desc}
-</帖子>
-
-请评估这个帖子与原始需求的匹配程度。
-"""
-        result = await Runner.run(result_evaluator, eval_input)
-        evaluation: ResultEvaluation = result.final_output
-
-        # 收集Result评估Agent的输入输出
-        result_eval_agent_call = {
-            "agent": "Result匹配度评估专家",
-            "action": "评估帖子匹配度",
-            "input": {
-                "note_id": note_data.get("note_id"),
-                "title": title,
-                "desc": desc  # 完整描述
-            },
-            "output": {
-                "match_level": evaluation.match_level,
-                "relevance_score": evaluation.relevance_score,
-                "missing_aspects": evaluation.missing_aspects,
-                "reason": evaluation.reason
-            }
-        }
-        agent_calls.append(result_eval_agent_call)
-
-        note_data["evaluation"] = {
-            "match_level": evaluation.match_level,
-            "relevance_score": evaluation.relevance_score,
-            "missing_aspects": evaluation.missing_aspects,
-            "reason": evaluation.reason
-        }
-
-        # 将所有评估过的帖子添加到演化图(包括satisfied、partial、unsatisfied)
-        add_note_to_graph(context, query, query_state.level, note_data)
-
-        if evaluation.match_level == "satisfied":
-            satisfied_notes.append(note_data)
-            print(f"      ✓ 满足: {title[:30] if len(title) > 30 else title}... (分数: {evaluation.relevance_score:.2f})")
-        elif evaluation.match_level == "partial":
-            partial_notes.append(note_data)
-            print(f"      ~ 部分: {title[:30] if len(title) > 30 else title}... (缺失: {', '.join(evaluation.missing_aspects[:2])})")
-        else:  # unsatisfied
-            print(f"      ✗ 不满足: {title[:30] if len(title) > 30 else title}... (分数: {evaluation.relevance_score:.2f})")
-
-    # 4. 处理满足的帖子:不再扩充分词库(避免无限扩张)
-    new_queries = []
-
-    if satisfied_notes:
-        print(f"\n    ✓ 找到 {len(satisfied_notes)} 个满足的帖子,不再提取关键词入库")
-        # 注释掉关键词提取逻辑,保持分词库稳定
-        # for note in satisfied_notes[:3]:
-        #     extract_input = f"""
-        # <帖子>
-        # 标题: {note['title']}
-        # 描述: {note['desc']}
-        # </帖子>
-        #
-        # 请提取核心关键词。
-        # """
-        #     result = await Runner.run(keyword_extractor, extract_input)
-        #     extraction: KeywordExtraction = result.final_output
-        #
-        #     # 添加新词到分词库,标记来源
-        #     note_id = note.get('note_id', 'unknown')
-        #     for keyword in extraction.keywords:
-        #         if keyword not in word_lib.words:
-        #             word_lib.add_word(keyword, source=f"note:{note_id}")
-        #             print(f"      + 新词入库: {keyword} (来源: {note_id})")
-
-    # 5. 处理部分匹配的帖子:改造query
-    if partial_notes and len(satisfied_notes) < 5:  # 如果满足的不够,基于部分匹配改进
-        print(f"\n    基于 {len(partial_notes)} 个部分匹配帖子改造query...")
-        # 收集所有缺失方面
-        all_missing = []
-        for note in partial_notes:
-            all_missing.extend(note["evaluation"]["missing_aspects"])
-
-        if all_missing:
-            improvement_input = f"""
-<当前Query>
-{query}
-</当前Query>
-
-<缺失的方面>
-{', '.join(set(all_missing))}
-</缺失的方面>
-
-请改造query使其包含这些缺失的内容。
-"""
-            result = await Runner.run(query_improver, improvement_input)
-            improvement: QueryImprovement = result.final_output
-
-            # 收集Query改造Agent的输入输出
-            improvement_agent_call = {
-                "agent": "Query改造专家",
-                "action": "基于缺失方面改造Query",
-                "input": {
-                    "query": query,
-                    "missing_aspects": list(set(all_missing))
-                },
-                "output": {
-                    "improved_query": improvement.improved_query,
-                    "added_aspects": improvement.added_aspects,
-                    "reasoning": improvement.reasoning
-                }
-            }
-            agent_calls.append(improvement_agent_call)
-
-            # 评估改进后的query
-            improved_eval = await evaluate_query_relevance(improvement.improved_query, original_need, query_state.relevance_score, context)
-
-            # 创建改进后的query state
-            new_state = QueryState(
-                query=improvement.improved_query,
-                level=query_state.level + 1,
-                relevance_score=improved_eval.relevance_score,
-                parent_query=query,
-                strategy="基于部分匹配改进"
-            )
-
-            # 添加到演化图(无论是否提升)
-            add_query_to_graph(
-                context,
-                new_state,
-                iteration,
-                evaluation_reason=improved_eval.reason,
-                is_selected=improved_eval.is_improved,
-                parent_level=query_state.level  # 父节点的层级
-            )
-
-            if improved_eval.is_improved:
-                print(f"      ✓ 改进: {improvement.improved_query} (添加: {', '.join(improvement.added_aspects[:2])})")
-                new_queries.append(new_state)
-            else:
-                print(f"      ✗ 改进: {improvement.improved_query} (分数: {improved_eval.relevance_score:.2f}, 未提升)")
-
-    # 6. Result分支的改写策略(向上抽象和同义改写)
-    # 如果搜索结果不理想且新queries不够,尝试改写当前query
-    if len(satisfied_notes) < 3 and len(new_queries) < 2:
-        print(f"\n    搜索结果不理想,尝试改写query...")
-
-        # 6.1 向上抽象
-        if len(new_queries) < 3:
-            rewrite_input_abstract = f"""
-<当前Query>
-{query}
-</当前Query>
-
-<改写要求>
-类型: abstract (向上抽象)
-</改写要求>
-
-请改写这个query。
-"""
-            result = await Runner.run(query_rewriter, rewrite_input_abstract)
-            rewrite: QueryRewrite = result.final_output
-
-            # 收集Result分支改写(抽象)Agent的输入输出
-            rewrite_agent_call = {
-                "agent": "Query改写专家",
-                "action": "向上抽象改写(Result分支)",
-                "input": {
-                    "query": query,
-                    "rewrite_type": "abstract"
-                },
-                "output": {
-                    "rewritten_query": rewrite.rewritten_query,
-                    "rewrite_type": rewrite.rewrite_type,
-                    "reasoning": rewrite.reasoning
-                }
-            }
-            agent_calls.append(rewrite_agent_call)
-
-            # 评估改写后的query
-            rewrite_eval = await evaluate_query_relevance(rewrite.rewritten_query, original_need, query_state.relevance_score, context)
-
-            # 创建改写后的query state
-            new_state = QueryState(
-                query=rewrite.rewritten_query,
-                level=query_state.level + 1,
-                relevance_score=rewrite_eval.relevance_score,
-                parent_query=query,
-                strategy="结果分支-抽象改写"
-            )
-
-            # 添加到演化图(无论是否提升)
-            add_query_to_graph(
-                context,
-                new_state,
-                iteration,
-                evaluation_reason=rewrite_eval.reason,
-                is_selected=rewrite_eval.is_improved,
-                parent_level=query_state.level  # 父节点的层级
-            )
-
-            if rewrite_eval.is_improved:
-                print(f"      ✓ 改写(抽象): {rewrite.rewritten_query} (分数: {rewrite_eval.relevance_score:.2f})")
-                new_queries.append(new_state)
-            else:
-                print(f"      ✗ 改写(抽象): {rewrite.rewritten_query} (分数: {rewrite_eval.relevance_score:.2f}, 未提升)")
-
-        # 6.2 同义改写
-        if len(new_queries) < 4:
-            rewrite_input_synonym = f"""
-<当前Query>
-{query}
-</当前Query>
-
-<改写要求>
-类型: synonym (同义改写)
-使用同义词或相关表达来改写query,保持语义相同但表达方式不同。
-</改写要求>
-
-请改写这个query。
-"""
-            result = await Runner.run(query_rewriter, rewrite_input_synonym)
-            rewrite_syn: QueryRewrite = result.final_output
-
-            # 收集Result分支改写(同义)Agent的输入输出
-            rewrite_syn_agent_call = {
-                "agent": "Query改写专家",
-                "action": "同义改写(Result分支)",
-                "input": {
-                    "query": query,
-                    "rewrite_type": "synonym"
-                },
-                "output": {
-                    "rewritten_query": rewrite_syn.rewritten_query,
-                    "rewrite_type": rewrite_syn.rewrite_type,
-                    "reasoning": rewrite_syn.reasoning
-                }
-            }
-            agent_calls.append(rewrite_syn_agent_call)
-
-            # 评估改写后的query
-            rewrite_syn_eval = await evaluate_query_relevance(rewrite_syn.rewritten_query, original_need, query_state.relevance_score, context)
-
-            # 创建改写后的query state
-            new_state = QueryState(
-                query=rewrite_syn.rewritten_query,
-                level=query_state.level + 1,
-                relevance_score=rewrite_syn_eval.relevance_score,
-                parent_query=query,
-                strategy="结果分支-同义改写"
-            )
-
-            # 添加到演化图(无论是否提升)
-            add_query_to_graph(
-                context,
-                new_state,
-                iteration,
-                evaluation_reason=rewrite_syn_eval.reason,
-                is_selected=rewrite_syn_eval.is_improved,
-                parent_level=query_state.level  # 父节点的层级
-            )
-
-            if rewrite_syn_eval.is_improved:
-                print(f"      ✓ 改写(同义): {rewrite_syn.rewritten_query} (分数: {rewrite_syn_eval.relevance_score:.2f})")
-                new_queries.append(new_state)
-            else:
-                print(f"      ✗ 改写(同义): {rewrite_syn.rewritten_query} (分数: {rewrite_syn_eval.relevance_score:.2f}, 未提升)")
-
-    # 记录完整的result分支处理结果(层级化)
-    add_step(context, f"Result分支 - {query}", "result_branch", {
-        "query": query,
-        "query_level": query_state.level,
-        "query_relevance": query_state.relevance_score,
-        "relevance_threshold": relevance_threshold,
-        "passed_threshold": query_state.relevance_score >= relevance_threshold,
-        "notes_count": len(notes) if 'notes' in locals() else 0,
-        "satisfied_count": len(satisfied_notes),
-        "partial_count": len(partial_notes),
-        "satisfied_notes": [
-            {
-                "note_id": note["note_id"],
-                "title": note["title"],
-                "score": note["evaluation"]["relevance_score"],
-                "match_level": note["evaluation"]["match_level"]
-            }
-            for note in satisfied_notes  # 保存所有满足的帖子
-        ],
-        "agent_calls": agent_calls,  # 所有Agent调用的详细记录
-        "new_queries_generated": len(new_queries),
-        "new_queries": [{"query": nq.query, "score": nq.relevance_score, "strategy": nq.strategy} for nq in new_queries]
-    })
-
-    return satisfied_notes, new_queries
-
-
-async def iterative_search_loop(
-    context: RunContext,
-    max_iterations: int = 20,
-    relevance_threshold: float = 0.6
-) -> list[dict]:
-    """
-    主循环:迭代搜索(按层级处理)
-
-    Args:
-        context: 运行上下文
-        max_iterations: 最大迭代次数(层级数)
-        relevance_threshold: 相关度门槛
-
-    Returns:
-        满足需求的帖子列表
-    """
-
-    print(f"\n{'='*60}")
-    print(f"开始迭代搜索循环")
-    print(f"{'='*60}")
-
-    # 0. 添加原始问题作为根节点
-    root_query_state = QueryState(
-        query=context.q,
-        level=0,
-        relevance_score=1.0,  # 原始问题本身相关度为1.0
-        strategy="根节点"
-    )
-    add_query_to_graph(context, root_query_state, 0, evaluation_reason="原始问题,作为搜索的根节点", is_selected=True)
-    print(f"[根节点] 原始问题: {context.q}")
-
-    # 1. 初始化分词库
-    word_lib = await initialize_word_library(context.q, context)
-
-    # 2. 初始化query队列 - 智能选择最相关的词
-    all_words = list(word_lib.words)
-    query_queue = []
-
-    print(f"\n评估所有初始分词的相关度...")
-    word_scores = []
-
-    for word in all_words:
-        # 评估每个词的相关度
-        eval_result = await evaluate_query_relevance(word, context.q, None, context)
-        word_scores.append({
-            'word': word,
-            'score': eval_result.relevance_score,
-            'eval': eval_result
-        })
-        print(f"  {word}: {eval_result.relevance_score:.2f}")
-
-    # 按相关度排序,使用所有分词
-    word_scores.sort(key=lambda x: x['score'], reverse=True)
-    selected_words = word_scores  # 使用所有分词
-
-    # 将所有分词添加到演化图(全部被选中)
-    for item in word_scores:
-        is_selected = True  # 所有分词都被选中
-        query_state = QueryState(
-            query=item['word'],
-            level=1,
-            relevance_score=item['score'],
-            strategy="初始分词",
-            parent_query=context.q  # 父节点是原始问题
-        )
-
-        # 添加到演化图(会自动创建从parent_query到该query的边)
-        add_query_to_graph(context, query_state, 0, evaluation_reason=item['eval'].reason, is_selected=is_selected, parent_level=0)  # 父节点是根节点(level 0)
-
-        # 只有被选中的才加入队列
-        if is_selected:
-            query_queue.append(query_state)
-
-    print(f"\n初始query队列(按相关度排序): {[(q.query, f'{q.relevance_score:.2f}') for q in query_queue]}")
-    print(f"  (共评估了 {len(word_scores)} 个分词,全部加入队列)")
-
-    # 3. API实例
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-    xiaohongshu_search = XiaohongshuSearch()
-
-    # 4. 主循环
-    all_satisfied_notes = []
-    iteration = 0
-
-    while query_queue and iteration < max_iterations:
-        iteration += 1
-
-        # 获取当前层级(队列中最小的level)
-        current_level = min(q.level for q in query_queue)
-
-        # 提取当前层级的所有query
-        current_batch = [q for q in query_queue if q.level == current_level]
-        query_queue = [q for q in query_queue if q.level != current_level]
-
-        print(f"\n{'='*60}")
-        print(f"迭代 {iteration}: 处理第 {current_level} 层,共 {len(current_batch)} 个query")
-        print(f"{'='*60}")
-
-        # 记录本轮处理的queries
-        add_step(context, f"迭代 {iteration}", "iteration", {
-            "iteration": iteration,
-            "current_level": current_level,
-            "current_batch_size": len(current_batch),
-            "remaining_queue_size": len(query_queue),
-            "processing_queries": [{"query": q.query, "level": q.level} for q in current_batch]
-        })
-
-        new_queries_from_sug = []
-        new_queries_from_result = []
-
-        # 处理每个query
-        for query_state in current_batch:
-            print(f"\n处理Query [{query_state.level}]: {query_state.query} (分数: {query_state.relevance_score:.2f})")
-
-            # 检查终止条件
-            if query_state.is_terminated or query_state.no_suggestion_rounds >= 2:
-                print(f"  ✗ 已终止或连续2轮无suggestion,跳过该query")
-                query_state.is_terminated = True
-                continue
-
-            # 并行处理两个分支
-            sug_task = process_suggestions(
-                query_state.query, query_state, context.q, word_lib, context, xiaohongshu_api, iteration
-            )
-            result_task = process_search_results(
-                query_state.query, query_state, context.q, word_lib, context,
-                xiaohongshu_search, relevance_threshold, iteration
-            )
-
-            # 等待两个分支完成
-            sug_queries, (satisfied_notes, result_queries) = await asyncio.gather(
-                sug_task,
-                result_task
-            )
-
-            # 如果suggestion分支返回空,说明没有获取到suggestion,需要继承no_suggestion_rounds
-            # 注意:process_suggestions内部已经更新了query_state.no_suggestion_rounds
-            # 所以这里生成的新queries需要继承父query的no_suggestion_rounds(如果sug分支也返回空)
-            if not sug_queries and not result_queries:
-                # 两个分支都没有产生新query,标记当前query为终止
-                query_state.is_terminated = True
-                print(f"  ⚠ 两个分支均未产生新query,标记该query为终止")
-
-            new_queries_from_sug.extend(sug_queries)
-            new_queries_from_result.extend(result_queries)
-            all_satisfied_notes.extend(satisfied_notes)
-
-        # 更新队列
-        all_new_queries = new_queries_from_sug + new_queries_from_result
-
-        # 注意:不需要在这里再次添加到演化图,因为在 process_suggestions 和 process_search_results 中已经添加过了
-        # 如果在这里再次调用 add_query_to_graph,会覆盖之前设置的 evaluation_reason 等字段
-
-        query_queue.extend(all_new_queries)
-
-        # 去重(基于query文本)并过滤已终止的query
-        seen = set()
-        unique_queue = []
-        for q in query_queue:
-            if q.query not in seen and not q.is_terminated:
-                seen.add(q.query)
-                unique_queue.append(q)
-        query_queue = unique_queue
-
-        # 按相关度排序
-        query_queue.sort(key=lambda x: x.relevance_score, reverse=True)
-
-        print(f"\n本轮结果:")
-        print(f"  新增满足帖子: {len(satisfied_notes)}")
-        print(f"  累计满足帖子: {len(all_satisfied_notes)}")
-        print(f"  新增queries: {len(all_new_queries)}")
-        print(f"  队列剩余: {len(query_queue)}")
-
-        # 更新分词库到context
-        context.word_library = word_lib.model_dump()
-
-        # 如果满足条件的帖子足够多,可以提前结束
-        if len(all_satisfied_notes) >= 20:
-            print(f"\n已找到足够的满足帖子 ({len(all_satisfied_notes)}个),提前结束")
-            break
-
-    print(f"\n{'='*60}")
-    print(f"迭代搜索完成")
-    print(f"  总迭代次数: {iteration}")
-    print(f"  最终满足帖子数: {len(all_satisfied_notes)}")
-    print(f"  核心词库: {list(word_lib.core_words)}")
-    print(f"  最终分词库大小: {len(word_lib.words)}")
-    print(f"{'='*60}")
-
-    # 保存最终结果
-    add_step(context, "迭代搜索完成", "loop_complete", {
-        "total_iterations": iteration,
-        "total_satisfied_notes": len(all_satisfied_notes),
-        "core_words": list(word_lib.core_words),
-        "final_word_library_size": len(word_lib.words),
-        "final_word_library": list(word_lib.words)
-    })
-
-    return all_satisfied_notes
-
-
-# ============================================================================
-# 主函数
-# ============================================================================
-
-async def main(input_dir: str, max_iterations: int = 20, visualize: bool = False):
-    """主函数"""
-    current_time, log_url = set_trace()
-
-    # 读取输入
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 版本信息
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]
-
-    # 日志目录
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    # 创建运行上下文
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-
-    # 执行迭代搜索
-    satisfied_notes = await iterative_search_loop(
-        run_context,
-        max_iterations=max_iterations,
-        relevance_threshold=0.6
-    )
-
-    # 保存结果
-    run_context.satisfied_notes = satisfied_notes
-
-    # 格式化输出
-    output = f"原始问题:{run_context.q}\n"
-    output += f"找到满足需求的帖子:{len(satisfied_notes)} 个\n"
-    output += f"核心词库:{', '.join(run_context.word_library.get('core_words', []))}\n"
-    output += f"分词库大小:{len(run_context.word_library.get('words', []))} 个词\n"
-    output += "\n" + "="*60 + "\n"
-
-    if satisfied_notes:
-        output += "【满足需求的帖子】\n\n"
-        for idx, note in enumerate(satisfied_notes, 1):
-            output += f"{idx}. {note['title']}\n"
-            output += f"   相关度: {note['evaluation']['relevance_score']:.2f}\n"
-            output += f"   URL: {note['note_url']}\n\n"
-    else:
-        output += "未找到满足需求的帖子\n"
-
-    run_context.final_output = output
-
-    print(f"\n{'='*60}")
-    print("最终结果")
-    print(f"{'='*60}")
-    print(output)
-
-    # 保存日志
-    os.makedirs(run_context.log_dir, exist_ok=True)
-
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    context_dict = run_context.model_dump()
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(context_dict, f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-    steps_file_path = os.path.join(run_context.log_dir, "steps.json")
-    with open(steps_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.steps, f, ensure_ascii=False, indent=2)
-    print(f"Steps log saved to: {steps_file_path}")
-
-    # 保存Query演化图
-    query_graph_file_path = os.path.join(run_context.log_dir, "query_graph.json")
-    with open(query_graph_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.query_graph, f, ensure_ascii=False, indent=2)
-    print(f"Query graph saved to: {query_graph_file_path}")
-
-    # 可视化
-    if visualize:
-        import subprocess
-        output_html = os.path.join(run_context.log_dir, "visualization.html")
-        print(f"\n🎨 生成可视化HTML...")
-
-        # 获取绝对路径
-        vis_script = os.path.abspath("visualization/sug_v6_1_2_5/index.js")
-        abs_query_graph = os.path.abspath(query_graph_file_path)
-        abs_output_html = os.path.abspath(output_html)
-
-        # 在可视化脚本目录中执行,确保使用本地 node_modules
-        result = subprocess.run([
-            "node", "index.js",
-            abs_query_graph,
-            abs_output_html
-        ], cwd="visualization/sug_v6_1_2_5")
-
-        if result.returncode == 0:
-            print(f"✅ 可视化已生成: {output_html}")
-        else:
-            print(f"❌ 可视化生成失败")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具 - v6.1.2.5 迭代循环版")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    parser.add_argument(
-        "--max-iterations",
-        type=int,
-        default=20,
-        help="最大迭代次数,默认: 20"
-    )
-    parser.add_argument(
-        "--visualize",
-        action="store_true",
-        default=False,
-        help="运行完成后自动生成可视化HTML"
-    )
-    parser.add_argument(
-        "--visualize-only",
-        type=str,
-        help="仅生成可视化,指定query_graph.json文件路径"
-    )
-    args = parser.parse_args()
-
-    # 如果只是生成可视化
-    if args.visualize_only:
-        import subprocess
-        query_graph_path = args.visualize_only
-        output_html = os.path.splitext(query_graph_path)[0].replace("query_graph", "visualization") + ".html"
-        if not output_html.endswith(".html"):
-            output_html = os.path.join(os.path.dirname(query_graph_path), "visualization.html")
-
-        print(f"🎨 生成可视化HTML...")
-        print(f"输入: {query_graph_path}")
-        print(f"输出: {output_html}")
-
-        # 获取绝对路径
-        abs_query_graph = os.path.abspath(query_graph_path)
-        abs_output_html = os.path.abspath(output_html)
-
-        # 在可视化脚本目录中执行,确保使用本地 node_modules
-        result = subprocess.run([
-            "node", "index.js",
-            abs_query_graph,
-            abs_output_html
-        ], cwd="visualization/sug_v6_1_2_5")
-
-        if result.returncode == 0:
-            print(f"✅ 可视化已生成: {output_html}")
-        else:
-            print(f"❌ 可视化生成失败")
-        sys.exit(result.returncode)
-
-    asyncio.run(main(args.input_dir, max_iterations=args.max_iterations, visualize=args.visualize))

+ 0 - 1551
sug_v6_1_2_6.py

@@ -1,1551 +0,0 @@
-import asyncio
-import json
-import os
-import sys
-import argparse
-from datetime import datetime
-from typing import Literal
-
-from agents import Agent, Runner
-from lib.my_trace import set_trace
-from pydantic import BaseModel, Field
-
-from lib.utils import read_file_as_string
-from lib.client import get_model
-MODEL_NAME = "google/gemini-2.5-flash"
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-from script.search.xiaohongshu_search import XiaohongshuSearch
-
-
-# ============================================================================
-# 数据模型
-# ============================================================================
-
-class QueryState(BaseModel):
-    """Query状态跟踪"""
-    query: str
-    level: int  # 当前所在层级
-    no_suggestion_rounds: int = 0  # 连续没有suggestion的轮数
-    relevance_score: float = 0.0  # 与原始需求的相关度
-    parent_query: str | None = None  # 父query
-    strategy: str | None = None  # 生成策略:direct_sug, rewrite, add_word
-    is_terminated: bool = False  # 是否已终止(不再处理)
-
-
-class WordLibrary(BaseModel):
-    """动态分词库"""
-    words: set[str] = Field(default_factory=set)
-    word_sources: dict[str, str] = Field(default_factory=dict)  # 记录词的来源:word -> source(note_id或"initial")
-    core_words: set[str] = Field(default_factory=set)  # 核心词(第一层初始分词)
-
-    def add_word(self, word: str, source: str = "unknown", is_core: bool = False):
-        """添加单词到分词库"""
-        if word and word.strip():
-            word = word.strip()
-            self.words.add(word)
-            if word not in self.word_sources:
-                self.word_sources[word] = source
-            if is_core:
-                self.core_words.add(word)
-
-    def add_words(self, words: list[str], source: str = "unknown", is_core: bool = False):
-        """批量添加单词"""
-        for word in words:
-            self.add_word(word, source, is_core)
-
-    def get_unused_word(self, current_query: str, prefer_core: bool = True) -> str | None:
-        """获取一个当前query中没有的词
-
-        Args:
-            current_query: 当前查询
-            prefer_core: 是否优先返回核心词(默认True)
-        """
-        # 优先从核心词中查找
-        if prefer_core and self.core_words:
-            for word in self.core_words:
-                if word not in current_query:
-                    return word
-
-        # 如果核心词都用完了,或者不优先使用核心词,从所有词中查找
-        for word in self.words:
-            if word not in current_query:
-                return word
-        return None
-
-    def model_dump(self):
-        """序列化为dict"""
-        return {
-            "words": list(self.words),
-            "word_sources": self.word_sources,
-            "core_words": list(self.core_words)
-        }
-
-
-class RunContext(BaseModel):
-    """运行上下文"""
-    version: str
-    input_files: dict[str, str]
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-
-    # 新增字段
-    word_library: dict = Field(default_factory=dict)  # 使用dict存储,因为set不能直接序列化
-    query_states: list[dict] = Field(default_factory=list)
-    steps: list[dict] = Field(default_factory=list)
-
-    # Query演化图
-    query_graph: dict = Field(default_factory=dict)  # 记录Query的演化路径和关系
-
-    # 最终结果
-    satisfied_notes: list[dict] = Field(default_factory=list)
-    final_output: str | None = None
-
-
-# ============================================================================
-# Agent 定义
-# ============================================================================
-
-# Agent 1: 分词专家
-class WordSegmentation(BaseModel):
-    """分词结果"""
-    words: list[str] = Field(..., description="分词结果列表")
-    reasoning: str = Field(..., description="分词理由")
-
-word_segmentation_instructions = """
-你是分词专家。给定一个query,将其拆分成有意义的最小单元。
-
-## 分词原则
-1. 保留有搜索意义的词汇
-2. 拆分成独立的概念
-3. 保留专业术语的完整性
-4. 去除虚词(的、吗、呢等)
-
-## 输出要求
-返回分词列表和分词理由。
-""".strip()
-
-word_segmenter = Agent[None](
-    name="分词专家",
-    instructions=word_segmentation_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=WordSegmentation,
-)
-
-
-# Agent 2: Query相关度评估专家
-class RelevanceEvaluation(BaseModel):
-    """相关度评估"""
-    relevance_score: float = Field(..., description="相关性分数 0-1")
-    is_improved: bool = Field(..., description="是否比之前更好")
-    reason: str = Field(..., description="评估理由")
-
-relevance_evaluation_instructions = """
-你是Query相关度评估专家。
-
-## 任务
-评估当前query与原始需求的匹配程度。
-
-## 评估标准
-- 主题相关性
-- 要素覆盖度
-- 意图匹配度
-
-## 输出
-- relevance_score: 0-1的相关性分数
-- is_improved: 如果提供了previous_score,判断是否有提升
-- reason: 详细理由
-""".strip()
-
-relevance_evaluator = Agent[None](
-    name="Query相关度评估专家",
-    instructions=relevance_evaluation_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=RelevanceEvaluation,
-)
-
-
-# Agent 3: Query改写专家
-class QueryRewrite(BaseModel):
-    """Query改写结果"""
-    rewritten_query: str = Field(..., description="改写后的query")
-    rewrite_type: str = Field(..., description="改写类型:abstract或synonym")
-    reasoning: str = Field(..., description="改写理由")
-
-query_rewrite_instructions = """
-你是Query改写专家。
-
-## 改写策略
-1. **向上抽象**:将具体概念泛化到更高层次
-   - 例:iPhone 13 → 智能手机
-2. **同义改写**:使用同义词或相关表达
-   - 例:购买 → 入手、获取
-
-## 输出要求
-返回改写后的query、改写类型和理由。
-""".strip()
-
-query_rewriter = Agent[None](
-    name="Query改写专家",
-    instructions=query_rewrite_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=QueryRewrite,
-)
-
-
-# Agent 4: 加词位置评估专家
-class WordInsertion(BaseModel):
-    """加词结果"""
-    new_query: str = Field(..., description="加词后的新query")
-    insertion_position: str = Field(..., description="插入位置描述")
-    reasoning: str = Field(..., description="插入理由")
-
-word_insertion_instructions = """
-你是加词位置评估专家。
-
-## 任务
-将新词加到当前query的最合适位置,保持语义通顺。
-
-## 原则
-1. 保持语法正确
-2. 语义连贯
-3. 符合搜索习惯
-
-## 输出
-返回新query、插入位置描述和理由。
-""".strip()
-
-word_inserter = Agent[None](
-    name="加词位置评估专家",
-    instructions=word_insertion_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=WordInsertion,
-)
-
-
-# Agent 5: Result匹配度评估专家
-class ResultEvaluation(BaseModel):
-    """Result评估结果"""
-    match_level: str = Field(..., description="匹配等级:satisfied, partial, unsatisfied")
-    relevance_score: float = Field(..., description="相关性分数 0-1")
-    missing_aspects: list[str] = Field(default_factory=list, description="缺失的方面")
-    reason: str = Field(..., description="评估理由")
-
-result_evaluation_instructions = """
-你是Result匹配度评估专家。
-
-## 任务
-评估搜索结果(帖子)与原始需求的匹配程度。
-
-## 评估等级
-1. **satisfied**: 完全满足需求
-2. **partial**: 部分满足,但有缺失
-3. **unsatisfied**: 基本不满足
-
-## 输出要求
-- match_level: 匹配等级
-- relevance_score: 相关性分数
-- missing_aspects: 如果是partial,列出缺失的方面
-- reason: 详细理由
-""".strip()
-
-result_evaluator = Agent[None](
-    name="Result匹配度评估专家",
-    instructions=result_evaluation_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=ResultEvaluation,
-)
-
-
-# Agent 6: Query改造专家(基于缺失部分)
-class QueryImprovement(BaseModel):
-    """Query改造结果"""
-    improved_query: str = Field(..., description="改造后的query")
-    added_aspects: list[str] = Field(..., description="添加的方面")
-    reasoning: str = Field(..., description="改造理由")
-
-query_improvement_instructions = """
-你是Query改造专家。
-
-## 任务
-根据搜索结果的缺失部分,改造query使其包含这些内容。
-
-## 原则
-1. 针对性补充缺失方面
-2. 保持query简洁
-3. 符合搜索习惯
-
-## 输出
-返回改造后的query、添加的方面和理由。
-""".strip()
-
-query_improver = Agent[None](
-    name="Query改造专家",
-    instructions=query_improvement_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=QueryImprovement,
-)
-
-
-# Agent 7: 关键词提取专家
-class KeywordExtraction(BaseModel):
-    """关键词提取结果"""
-    keywords: list[str] = Field(..., description="提取的关键词列表")
-    reasoning: str = Field(..., description="提取理由")
-
-keyword_extraction_instructions = """
-你是关键词提取专家。
-
-## 任务
-从帖子标题和描述中提取核心关键词。
-
-## 提取原则
-1. 提取有搜索价值的词汇
-2. 去除虚词和通用词
-3. 保留专业术语
-4. 提取3-10个关键词
-
-## 输出
-返回关键词列表和提取理由。
-""".strip()
-
-keyword_extractor = Agent[None](
-    name="关键词提取专家",
-    instructions=keyword_extraction_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=KeywordExtraction,
-)
-
-
-# ============================================================================
-# 辅助函数
-# ============================================================================
-
-def add_step(context: RunContext, step_name: str, step_type: str, data: dict):
-    """添加步骤记录"""
-    step = {
-        "step_number": len(context.steps) + 1,
-        "step_name": step_name,
-        "step_type": step_type,
-        "timestamp": datetime.now().isoformat(),
-        "data": data
-    }
-    context.steps.append(step)
-    return step
-
-
-def add_query_to_graph(context: RunContext, query_state: QueryState, iteration: int, evaluation_reason: str = "", is_selected: bool = True, parent_level: int | None = None):
-    """添加Query节点到演化图
-
-    Args:
-        context: 运行上下文
-        query_state: Query状态
-        iteration: 迭代次数
-        evaluation_reason: 评估原因(可选)
-        is_selected: 是否被选中进入处理队列(默认True)
-        parent_level: 父节点的层级(用于构造parent_id)
-    """
-    # 使用 "query_level" 格式作为节点ID
-    query_id = f"{query_state.query}_{query_state.level}"
-
-    # 初始化图结构
-    if "nodes" not in context.query_graph:
-        context.query_graph["nodes"] = {}
-        context.query_graph["edges"] = []
-        context.query_graph["iterations"] = {}
-
-    # 添加Query节点(type: query)
-    context.query_graph["nodes"][query_id] = {
-        "type": "query",
-        "query": query_state.query,
-        "level": query_state.level,
-        "relevance_score": query_state.relevance_score,
-        "strategy": query_state.strategy,
-        "parent_query": query_state.parent_query,
-        "iteration": iteration,
-        "is_terminated": query_state.is_terminated,
-        "no_suggestion_rounds": query_state.no_suggestion_rounds,
-        "evaluation_reason": evaluation_reason,  # 评估原因
-        "is_selected": is_selected  # 是否被选中
-    }
-
-    # 添加边(父子关系)
-    if query_state.parent_query and parent_level is not None:
-        # 构造父节点ID: parent_query_parent_level
-        parent_id = f"{query_state.parent_query}_{parent_level}"
-        if parent_id in context.query_graph["nodes"]:
-            context.query_graph["edges"].append({
-                "from": parent_id,
-                "to": query_id,
-                "edge_type": "query_to_query",
-                "strategy": query_state.strategy,
-                "score_improvement": query_state.relevance_score - context.query_graph["nodes"][parent_id]["relevance_score"]
-            })
-
-    # 按迭代分组
-    if iteration not in context.query_graph["iterations"]:
-        context.query_graph["iterations"][iteration] = []
-    context.query_graph["iterations"][iteration].append(query_id)
-
-
-def add_note_to_graph(context: RunContext, query: str, query_level: int, note: dict):
-    """添加Note节点到演化图,并连接到对应的Query
-
-    Args:
-        context: 运行上下文
-        query: query文本
-        query_level: query所在层级
-        note: 帖子数据
-    """
-    note_id = note["note_id"]
-
-    # 初始化图结构
-    if "nodes" not in context.query_graph:
-        context.query_graph["nodes"] = {}
-        context.query_graph["edges"] = []
-        context.query_graph["iterations"] = {}
-
-    # 添加Note节点(type: note),包含完整的元信息
-    context.query_graph["nodes"][note_id] = {
-        "type": "note",
-        "note_id": note_id,
-        "title": note["title"],
-        "desc": note.get("desc", ""),  # 完整描述,不截断
-        "note_url": note.get("note_url", ""),
-        "image_list": note.get("image_list", []),  # 图片列表
-        "interact_info": note.get("interact_info", {}),  # 互动信息(点赞、收藏、评论、分享)
-        "match_level": note["evaluation"]["match_level"],
-        "relevance_score": note["evaluation"]["relevance_score"],
-        "evaluation_reason": note["evaluation"].get("reason", ""),  # 评估原因
-        "found_by_query": query
-    }
-
-    # 添加边:Query → Note,使用 query_level 格式的ID
-    query_id = f"{query}_{query_level}"
-    if query_id in context.query_graph["nodes"]:
-        context.query_graph["edges"].append({
-            "from": query_id,
-            "to": note_id,
-            "edge_type": "query_to_note",
-            "match_level": note["evaluation"]["match_level"],
-            "relevance_score": note["evaluation"]["relevance_score"]
-        })
-
-
-def process_note_data(note: dict) -> dict:
-    """处理搜索接口返回的帖子数据"""
-    note_card = note.get("note_card", {})
-    image_list = note_card.get("image_list", [])
-    interact_info = note_card.get("interact_info", {})
-    user_info = note_card.get("user", {})
-
-    return {
-        "note_id": note.get("id", ""),
-        "title": note_card.get("display_title", ""),
-        "desc": note_card.get("desc", ""),
-        "image_list": image_list,
-        "interact_info": {
-            "liked_count": interact_info.get("liked_count", 0),
-            "collected_count": interact_info.get("collected_count", 0),
-            "comment_count": interact_info.get("comment_count", 0),
-            "shared_count": interact_info.get("shared_count", 0)
-        },
-        "user": {
-            "nickname": user_info.get("nickname", ""),
-            "user_id": user_info.get("user_id", "")
-        },
-        "type": note_card.get("type", "normal"),
-        "note_url": f"https://www.xiaohongshu.com/explore/{note.get('id', '')}"
-    }
-
-
-# ============================================================================
-# 核心流程函数
-# ============================================================================
-
-async def initialize_word_library(original_query: str, context: RunContext) -> WordLibrary:
-    """初始化分词库"""
-    print("\n[初始化] 创建分词库...")
-
-    # 使用Agent进行分词
-    result = await Runner.run(word_segmenter, original_query)
-    segmentation: WordSegmentation = result.final_output
-
-    word_lib = WordLibrary()
-    # 初始分词标记为核心词(is_core=True)
-    word_lib.add_words(segmentation.words, source="initial", is_core=True)
-
-    print(f"初始分词库(核心词): {list(word_lib.words)}")
-    print(f"分词理由: {segmentation.reasoning}")
-
-    # 保存到context
-    context.word_library = word_lib.model_dump()
-
-    add_step(context, "初始化分词库", "word_library_init", {
-        "agent": "分词专家",
-        "input": original_query,
-        "output": {
-            "words": segmentation.words,
-            "reasoning": segmentation.reasoning
-        },
-        "result": {
-            "word_library": list(word_lib.words)
-        }
-    })
-
-    return word_lib
-
-
-async def evaluate_query_relevance(
-    query: str,
-    original_need: str,
-    previous_score: float | None = None,
-    context: RunContext = None
-) -> RelevanceEvaluation:
-    """评估query与原始需求的相关度"""
-
-    eval_input = f"""
-<原始需求>
-{original_need}
-</原始需求>
-
-<当前Query>
-{query}
-</当前Query>
-
-{"<之前的相关度分数>" + str(previous_score) + "</之前的相关度分数>" if previous_score is not None else ""}
-
-请评估当前query与原始需求的相关度。
-"""
-
-    result = await Runner.run(relevance_evaluator, eval_input)
-    evaluation: RelevanceEvaluation = result.final_output
-
-    return evaluation
-
-
-async def process_suggestions(
-    query: str,
-    query_state: QueryState,
-    original_need: str,
-    word_lib: WordLibrary,
-    context: RunContext,
-    xiaohongshu_api: XiaohongshuSearchRecommendations,
-    iteration: int
-) -> list[QueryState]:
-    """处理suggestion分支,返回新的query states"""
-
-    print(f"\n  [Suggestion分支] 处理query: {query}")
-
-    # 收集本次分支处理中的所有Agent调用
-    agent_calls = []
-
-    # 1. 获取suggestions
-    suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-
-    if not suggestions or len(suggestions) == 0:
-        print(f"    → 没有获取到suggestion")
-        query_state.no_suggestion_rounds += 1
-
-        # 记录步骤
-        add_step(context, f"Suggestion分支 - {query}", "suggestion_branch", {
-            "query": query,
-            "query_level": query_state.level,
-            "suggestions_count": 0,
-            "no_suggestion_rounds": query_state.no_suggestion_rounds,
-            "new_queries_generated": 0
-        })
-
-        return []
-
-    print(f"    → 获取到 {len(suggestions)} 个suggestions")
-    query_state.no_suggestion_rounds = 0  # 重置计数
-
-    # 2. 评估每个suggestion
-    new_queries = []
-    suggestion_evaluations = []
-
-    for sug in suggestions:  # 处理所有建议
-        # 评估sug与原始需求的相关度(注意:这里是与原始需求original_need对比,而非当前query)
-        # 这样可以确保生成的suggestion始终围绕用户的核心需求
-        sug_eval = await evaluate_query_relevance(sug, original_need, query_state.relevance_score, context)
-
-        sug_eval_record = {
-            "suggestion": sug,
-            "relevance_score": sug_eval.relevance_score,
-            "is_improved": sug_eval.is_improved,
-            "reason": sug_eval.reason
-        }
-        suggestion_evaluations.append(sug_eval_record)
-
-        # 创建query state(所有suggestion都作为query节点)
-        sug_state = QueryState(
-            query=sug,
-            level=query_state.level + 1,
-            relevance_score=sug_eval.relevance_score,
-            parent_query=query,
-            strategy="调用sug"
-        )
-
-        # 判断是否比当前query更好(只有提升的才加入待处理队列)
-        is_selected = sug_eval.is_improved and sug_eval.relevance_score > query_state.relevance_score
-
-        # 将所有suggestion添加到演化图(包括未提升的)
-        add_query_to_graph(
-            context,
-            sug_state,
-            iteration,
-            evaluation_reason=sug_eval.reason,
-            is_selected=is_selected,
-            parent_level=query_state.level  # 父节点的层级
-        )
-
-        if is_selected:
-            print(f"      ✓ {sug} (分数: {sug_eval.relevance_score:.2f}, 提升: {sug_eval.is_improved})")
-            new_queries.append(sug_state)
-        else:
-            print(f"      ✗ {sug} (分数: {sug_eval.relevance_score:.2f}, 未提升)")
-
-    # 3. 改写策略(向上抽象或同义改写)
-    if len(new_queries) < 3:  # 如果直接使用sug的数量不够,尝试改写
-        # 尝试向上抽象
-        rewrite_input_abstract = f"""
-<当前Query>
-{query}
-</当前Query>
-
-<改写要求>
-类型: abstract (向上抽象)
-</改写要求>
-
-请改写这个query。
-"""
-        result = await Runner.run(query_rewriter, rewrite_input_abstract)
-        rewrite: QueryRewrite = result.final_output
-
-        # 收集改写Agent的输入输出
-        rewrite_agent_call = {
-            "agent": "Query改写专家",
-            "action": "向上抽象改写",
-            "input": {
-                "query": query,
-                "rewrite_type": "abstract"
-            },
-            "output": {
-                "rewritten_query": rewrite.rewritten_query,
-                "rewrite_type": rewrite.rewrite_type,
-                "reasoning": rewrite.reasoning
-            }
-        }
-        agent_calls.append(rewrite_agent_call)
-
-        # 评估改写后的query
-        rewrite_eval = await evaluate_query_relevance(rewrite.rewritten_query, original_need, query_state.relevance_score, context)
-
-        # 创建改写后的query state
-        new_state = QueryState(
-            query=rewrite.rewritten_query,
-            level=query_state.level + 1,
-            relevance_score=rewrite_eval.relevance_score,
-            parent_query=query,
-            strategy="抽象改写"
-        )
-
-        # 添加到演化图(无论是否提升)
-        add_query_to_graph(
-            context,
-            new_state,
-            iteration,
-            evaluation_reason=rewrite_eval.reason,
-            is_selected=rewrite_eval.is_improved,
-            parent_level=query_state.level  # 父节点的层级
-        )
-
-        if rewrite_eval.is_improved:
-            print(f"      ✓ 改写(抽象): {rewrite.rewritten_query} (分数: {rewrite_eval.relevance_score:.2f})")
-            new_queries.append(new_state)
-        else:
-            print(f"      ✗ 改写(抽象): {rewrite.rewritten_query} (分数: {rewrite_eval.relevance_score:.2f}, 未提升)")
-
-    # 3.2. 同义改写策略
-    if len(new_queries) < 4:  # 如果还不够,尝试同义改写
-        rewrite_input_synonym = f"""
-<当前Query>
-{query}
-</当前Query>
-
-<改写要求>
-类型: synonym (同义改写)
-使用同义词或相关表达来改写query,保持语义相同但表达方式不同。
-</改写要求>
-
-请改写这个query。
-"""
-        result = await Runner.run(query_rewriter, rewrite_input_synonym)
-        rewrite_syn: QueryRewrite = result.final_output
-
-        # 收集同义改写Agent的输入输出
-        rewrite_syn_agent_call = {
-            "agent": "Query改写专家",
-            "action": "同义改写",
-            "input": {
-                "query": query,
-                "rewrite_type": "synonym"
-            },
-            "output": {
-                "rewritten_query": rewrite_syn.rewritten_query,
-                "rewrite_type": rewrite_syn.rewrite_type,
-                "reasoning": rewrite_syn.reasoning
-            }
-        }
-        agent_calls.append(rewrite_syn_agent_call)
-
-        # 评估改写后的query
-        rewrite_syn_eval = await evaluate_query_relevance(rewrite_syn.rewritten_query, original_need, query_state.relevance_score, context)
-
-        # 创建改写后的query state
-        new_state = QueryState(
-            query=rewrite_syn.rewritten_query,
-            level=query_state.level + 1,
-            relevance_score=rewrite_syn_eval.relevance_score,
-            parent_query=query,
-            strategy="同义改写"
-        )
-
-        # 添加到演化图(无论是否提升)
-        add_query_to_graph(
-            context,
-            new_state,
-            iteration,
-            evaluation_reason=rewrite_syn_eval.reason,
-            is_selected=rewrite_syn_eval.is_improved,
-            parent_level=query_state.level  # 父节点的层级
-        )
-
-        if rewrite_syn_eval.is_improved:
-            print(f"      ✓ 改写(同义): {rewrite_syn.rewritten_query} (分数: {rewrite_syn_eval.relevance_score:.2f})")
-            new_queries.append(new_state)
-        else:
-            print(f"      ✗ 改写(同义): {rewrite_syn.rewritten_query} (分数: {rewrite_syn_eval.relevance_score:.2f}, 未提升)")
-
-    # 4. 加词策略(优先使用核心词)
-    unused_word = word_lib.get_unused_word(query, prefer_core=True)
-    is_core_word = unused_word in word_lib.core_words if unused_word else False
-
-    if unused_word and len(new_queries) < 5:
-        word_type = "核心词" if is_core_word else "普通词"
-        insertion_input = f"""
-<当前Query>
-{query}
-</当前Query>
-
-<要添加的词>
-{unused_word}
-</要添加的词>
-
-请将这个词加到query的最合适位置。
-"""
-        result = await Runner.run(word_inserter, insertion_input)
-        insertion: WordInsertion = result.final_output
-
-        # 收集加词Agent的输入输出
-        insertion_agent_call = {
-            "agent": "加词位置评估专家",
-            "action": f"加词({word_type})",
-            "input": {
-                "query": query,
-                "word_to_add": unused_word,
-                "is_core_word": is_core_word
-            },
-            "output": {
-                "new_query": insertion.new_query,
-                "insertion_position": insertion.insertion_position,
-                "reasoning": insertion.reasoning
-            }
-        }
-        agent_calls.append(insertion_agent_call)
-
-        # 评估加词后的query
-        insertion_eval = await evaluate_query_relevance(insertion.new_query, original_need, query_state.relevance_score, context)
-
-        # 创建加词后的query state
-        new_state = QueryState(
-            query=insertion.new_query,
-            level=query_state.level + 1,
-            relevance_score=insertion_eval.relevance_score,
-            parent_query=query,
-            strategy="加词"
-        )
-
-        # 添加到演化图(无论是否提升)
-        add_query_to_graph(
-            context,
-            new_state,
-            iteration,
-            evaluation_reason=insertion_eval.reason,
-            is_selected=insertion_eval.is_improved,
-            parent_level=query_state.level  # 父节点的层级
-        )
-
-        if insertion_eval.is_improved:
-            print(f"      ✓ 加词({word_type}): {insertion.new_query} [+{unused_word}] (分数: {insertion_eval.relevance_score:.2f})")
-            new_queries.append(new_state)
-        else:
-            print(f"      ✗ 加词({word_type}): {insertion.new_query} [+{unused_word}] (分数: {insertion_eval.relevance_score:.2f}, 未提升)")
-
-    # 记录完整的suggestion分支处理结果(层级化)
-    add_step(context, f"Suggestion分支 - {query}", "suggestion_branch", {
-        "query": query,
-        "query_level": query_state.level,
-        "query_relevance": query_state.relevance_score,
-        "suggestions_count": len(suggestions),
-        "suggestions_evaluated": len(suggestion_evaluations),
-        "suggestion_evaluations": suggestion_evaluations,  # 保存所有评估
-        "agent_calls": agent_calls,  # 所有Agent调用的详细记录
-        "new_queries_generated": len(new_queries),
-        "new_queries": [{"query": nq.query, "score": nq.relevance_score, "strategy": nq.strategy} for nq in new_queries],
-        "no_suggestion_rounds": query_state.no_suggestion_rounds
-    })
-
-    return new_queries
-
-
-async def process_search_results(
-    query: str,
-    query_state: QueryState,
-    original_need: str,
-    word_lib: WordLibrary,
-    context: RunContext,
-    xiaohongshu_search: XiaohongshuSearch,
-    relevance_threshold: float,
-    iteration: int
-) -> tuple[list[dict], list[QueryState]]:
-    """
-    处理搜索结果分支
-    返回: (满足需求的notes, 需要继续迭代的新queries)
-    """
-
-    print(f"\n  [Result分支] 搜索query: {query}")
-
-    # 收集本次分支处理中的所有Agent调用
-    agent_calls = []
-
-    # 1. 判断query相关度是否达到门槛
-    if query_state.relevance_score < relevance_threshold:
-        print(f"    ✗ 相关度 {query_state.relevance_score:.2f} 低于门槛 {relevance_threshold},跳过搜索")
-        return [], []
-
-    print(f"    ✓ 相关度 {query_state.relevance_score:.2f} 达到门槛,执行搜索")
-
-    # 2. 执行搜索
-    try:
-        search_result = xiaohongshu_search.search(keyword=query)
-        result_str = search_result.get("result", "{}")
-        if isinstance(result_str, str):
-            result_data = json.loads(result_str)
-        else:
-            result_data = result_str
-
-        notes = result_data.get("data", {}).get("data", [])
-        print(f"    → 搜索到 {len(notes)} 个帖子")
-
-    except Exception as e:
-        print(f"    ✗ 搜索失败: {e}")
-        return [], []
-
-    if not notes:
-        return [], []
-
-    # 3. 评估每个帖子
-    satisfied_notes = []
-    partial_notes = []
-
-    for note in notes:  # 评估所有帖子
-        note_data = process_note_data(note)
-        title = note_data["title"] or ""
-        desc = note_data["desc"] or ""
-
-        # 跳过空标题和描述的帖子
-        if not title and not desc:
-            continue
-
-        # 评估帖子
-        eval_input = f"""
-<原始需求>
-{original_need}
-</原始需求>
-
-<帖子>
-标题: {title}
-描述: {desc}
-</帖子>
-
-请评估这个帖子与原始需求的匹配程度。
-"""
-        result = await Runner.run(result_evaluator, eval_input)
-        evaluation: ResultEvaluation = result.final_output
-
-        # 收集Result评估Agent的输入输出
-        result_eval_agent_call = {
-            "agent": "Result匹配度评估专家",
-            "action": "评估帖子匹配度",
-            "input": {
-                "note_id": note_data.get("note_id"),
-                "title": title,
-                "desc": desc  # 完整描述
-            },
-            "output": {
-                "match_level": evaluation.match_level,
-                "relevance_score": evaluation.relevance_score,
-                "missing_aspects": evaluation.missing_aspects,
-                "reason": evaluation.reason
-            }
-        }
-        agent_calls.append(result_eval_agent_call)
-
-        note_data["evaluation"] = {
-            "match_level": evaluation.match_level,
-            "relevance_score": evaluation.relevance_score,
-            "missing_aspects": evaluation.missing_aspects,
-            "reason": evaluation.reason
-        }
-
-        # 将所有评估过的帖子添加到演化图(包括satisfied、partial、unsatisfied)
-        add_note_to_graph(context, query, query_state.level, note_data)
-
-        if evaluation.match_level == "satisfied":
-            satisfied_notes.append(note_data)
-            print(f"      ✓ 满足: {title[:30] if len(title) > 30 else title}... (分数: {evaluation.relevance_score:.2f})")
-        elif evaluation.match_level == "partial":
-            partial_notes.append(note_data)
-            print(f"      ~ 部分: {title[:30] if len(title) > 30 else title}... (缺失: {', '.join(evaluation.missing_aspects[:2])})")
-        else:  # unsatisfied
-            print(f"      ✗ 不满足: {title[:30] if len(title) > 30 else title}... (分数: {evaluation.relevance_score:.2f})")
-
-    # 4. 处理满足的帖子:不再扩充分词库(避免无限扩张)
-    new_queries = []
-
-    if satisfied_notes:
-        print(f"\n    ✓ 找到 {len(satisfied_notes)} 个满足的帖子,不再提取关键词入库")
-        # 注释掉关键词提取逻辑,保持分词库稳定
-        # for note in satisfied_notes[:3]:
-        #     extract_input = f"""
-        # <帖子>
-        # 标题: {note['title']}
-        # 描述: {note['desc']}
-        # </帖子>
-        #
-        # 请提取核心关键词。
-        # """
-        #     result = await Runner.run(keyword_extractor, extract_input)
-        #     extraction: KeywordExtraction = result.final_output
-        #
-        #     # 添加新词到分词库,标记来源
-        #     note_id = note.get('note_id', 'unknown')
-        #     for keyword in extraction.keywords:
-        #         if keyword not in word_lib.words:
-        #             word_lib.add_word(keyword, source=f"note:{note_id}")
-        #             print(f"      + 新词入库: {keyword} (来源: {note_id})")
-
-    # 5. 处理部分匹配的帖子:改造query
-    if partial_notes and len(satisfied_notes) < 5:  # 如果满足的不够,基于部分匹配改进
-        print(f"\n    基于 {len(partial_notes)} 个部分匹配帖子改造query...")
-        # 收集所有缺失方面
-        all_missing = []
-        for note in partial_notes:
-            all_missing.extend(note["evaluation"]["missing_aspects"])
-
-        if all_missing:
-            improvement_input = f"""
-<当前Query>
-{query}
-</当前Query>
-
-<缺失的方面>
-{', '.join(set(all_missing))}
-</缺失的方面>
-
-请改造query使其包含这些缺失的内容。
-"""
-            result = await Runner.run(query_improver, improvement_input)
-            improvement: QueryImprovement = result.final_output
-
-            # 收集Query改造Agent的输入输出
-            improvement_agent_call = {
-                "agent": "Query改造专家",
-                "action": "基于缺失方面改造Query",
-                "input": {
-                    "query": query,
-                    "missing_aspects": list(set(all_missing))
-                },
-                "output": {
-                    "improved_query": improvement.improved_query,
-                    "added_aspects": improvement.added_aspects,
-                    "reasoning": improvement.reasoning
-                }
-            }
-            agent_calls.append(improvement_agent_call)
-
-            # 评估改进后的query
-            improved_eval = await evaluate_query_relevance(improvement.improved_query, original_need, query_state.relevance_score, context)
-
-            # 创建改进后的query state
-            new_state = QueryState(
-                query=improvement.improved_query,
-                level=query_state.level + 1,
-                relevance_score=improved_eval.relevance_score,
-                parent_query=query,
-                strategy="基于部分匹配改进"
-            )
-
-            # 添加到演化图(无论是否提升)
-            add_query_to_graph(
-                context,
-                new_state,
-                iteration,
-                evaluation_reason=improved_eval.reason,
-                is_selected=improved_eval.is_improved,
-                parent_level=query_state.level  # 父节点的层级
-            )
-
-            if improved_eval.is_improved:
-                print(f"      ✓ 改进: {improvement.improved_query} (添加: {', '.join(improvement.added_aspects[:2])})")
-                new_queries.append(new_state)
-            else:
-                print(f"      ✗ 改进: {improvement.improved_query} (分数: {improved_eval.relevance_score:.2f}, 未提升)")
-
-    # 6. Result分支的改写策略(向上抽象和同义改写)
-    # 如果搜索结果不理想且新queries不够,尝试改写当前query
-    if len(satisfied_notes) < 3 and len(new_queries) < 2:
-        print(f"\n    搜索结果不理想,尝试改写query...")
-
-        # 6.1 向上抽象
-        if len(new_queries) < 3:
-            rewrite_input_abstract = f"""
-<当前Query>
-{query}
-</当前Query>
-
-<改写要求>
-类型: abstract (向上抽象)
-</改写要求>
-
-请改写这个query。
-"""
-            result = await Runner.run(query_rewriter, rewrite_input_abstract)
-            rewrite: QueryRewrite = result.final_output
-
-            # 收集Result分支改写(抽象)Agent的输入输出
-            rewrite_agent_call = {
-                "agent": "Query改写专家",
-                "action": "向上抽象改写(Result分支)",
-                "input": {
-                    "query": query,
-                    "rewrite_type": "abstract"
-                },
-                "output": {
-                    "rewritten_query": rewrite.rewritten_query,
-                    "rewrite_type": rewrite.rewrite_type,
-                    "reasoning": rewrite.reasoning
-                }
-            }
-            agent_calls.append(rewrite_agent_call)
-
-            # 评估改写后的query
-            rewrite_eval = await evaluate_query_relevance(rewrite.rewritten_query, original_need, query_state.relevance_score, context)
-
-            # 创建改写后的query state
-            new_state = QueryState(
-                query=rewrite.rewritten_query,
-                level=query_state.level + 1,
-                relevance_score=rewrite_eval.relevance_score,
-                parent_query=query,
-                strategy="结果分支-抽象改写"
-            )
-
-            # 添加到演化图(无论是否提升)
-            add_query_to_graph(
-                context,
-                new_state,
-                iteration,
-                evaluation_reason=rewrite_eval.reason,
-                is_selected=rewrite_eval.is_improved,
-                parent_level=query_state.level  # 父节点的层级
-            )
-
-            if rewrite_eval.is_improved:
-                print(f"      ✓ 改写(抽象): {rewrite.rewritten_query} (分数: {rewrite_eval.relevance_score:.2f})")
-                new_queries.append(new_state)
-            else:
-                print(f"      ✗ 改写(抽象): {rewrite.rewritten_query} (分数: {rewrite_eval.relevance_score:.2f}, 未提升)")
-
-        # 6.2 同义改写
-        if len(new_queries) < 4:
-            rewrite_input_synonym = f"""
-<当前Query>
-{query}
-</当前Query>
-
-<改写要求>
-类型: synonym (同义改写)
-使用同义词或相关表达来改写query,保持语义相同但表达方式不同。
-</改写要求>
-
-请改写这个query。
-"""
-            result = await Runner.run(query_rewriter, rewrite_input_synonym)
-            rewrite_syn: QueryRewrite = result.final_output
-
-            # 收集Result分支改写(同义)Agent的输入输出
-            rewrite_syn_agent_call = {
-                "agent": "Query改写专家",
-                "action": "同义改写(Result分支)",
-                "input": {
-                    "query": query,
-                    "rewrite_type": "synonym"
-                },
-                "output": {
-                    "rewritten_query": rewrite_syn.rewritten_query,
-                    "rewrite_type": rewrite_syn.rewrite_type,
-                    "reasoning": rewrite_syn.reasoning
-                }
-            }
-            agent_calls.append(rewrite_syn_agent_call)
-
-            # 评估改写后的query
-            rewrite_syn_eval = await evaluate_query_relevance(rewrite_syn.rewritten_query, original_need, query_state.relevance_score, context)
-
-            # 创建改写后的query state
-            new_state = QueryState(
-                query=rewrite_syn.rewritten_query,
-                level=query_state.level + 1,
-                relevance_score=rewrite_syn_eval.relevance_score,
-                parent_query=query,
-                strategy="结果分支-同义改写"
-            )
-
-            # 添加到演化图(无论是否提升)
-            add_query_to_graph(
-                context,
-                new_state,
-                iteration,
-                evaluation_reason=rewrite_syn_eval.reason,
-                is_selected=rewrite_syn_eval.is_improved,
-                parent_level=query_state.level  # 父节点的层级
-            )
-
-            if rewrite_syn_eval.is_improved:
-                print(f"      ✓ 改写(同义): {rewrite_syn.rewritten_query} (分数: {rewrite_syn_eval.relevance_score:.2f})")
-                new_queries.append(new_state)
-            else:
-                print(f"      ✗ 改写(同义): {rewrite_syn.rewritten_query} (分数: {rewrite_syn_eval.relevance_score:.2f}, 未提升)")
-
-    # 记录完整的result分支处理结果(层级化)
-    add_step(context, f"Result分支 - {query}", "result_branch", {
-        "query": query,
-        "query_level": query_state.level,
-        "query_relevance": query_state.relevance_score,
-        "relevance_threshold": relevance_threshold,
-        "passed_threshold": query_state.relevance_score >= relevance_threshold,
-        "notes_count": len(notes) if 'notes' in locals() else 0,
-        "satisfied_count": len(satisfied_notes),
-        "partial_count": len(partial_notes),
-        "satisfied_notes": [
-            {
-                "note_id": note["note_id"],
-                "title": note["title"],
-                "score": note["evaluation"]["relevance_score"],
-                "match_level": note["evaluation"]["match_level"]
-            }
-            for note in satisfied_notes  # 保存所有满足的帖子
-        ],
-        "agent_calls": agent_calls,  # 所有Agent调用的详细记录
-        "new_queries_generated": len(new_queries),
-        "new_queries": [{"query": nq.query, "score": nq.relevance_score, "strategy": nq.strategy} for nq in new_queries]
-    })
-
-    return satisfied_notes, new_queries
-
-
-async def iterative_search_loop(
-    context: RunContext,
-    max_iterations: int = 20,
-    relevance_threshold: float = 0.6
-) -> list[dict]:
-    """
-    主循环:迭代搜索(按层级处理)
-
-    Args:
-        context: 运行上下文
-        max_iterations: 最大迭代次数(层级数)
-        relevance_threshold: 相关度门槛
-
-    Returns:
-        满足需求的帖子列表
-    """
-
-    print(f"\n{'='*60}")
-    print(f"开始迭代搜索循环")
-    print(f"{'='*60}")
-
-    # 0. 添加原始问题作为根节点
-    root_query_state = QueryState(
-        query=context.q,
-        level=0,
-        relevance_score=1.0,  # 原始问题本身相关度为1.0
-        strategy="根节点"
-    )
-    add_query_to_graph(context, root_query_state, 0, evaluation_reason="原始问题,作为搜索的根节点", is_selected=True)
-    print(f"[根节点] 原始问题: {context.q}")
-
-    # 1. 初始化分词库
-    word_lib = await initialize_word_library(context.q, context)
-
-    # 2. 初始化query队列 - 智能选择最相关的词
-    all_words = list(word_lib.words)
-    query_queue = []
-
-    print(f"\n评估所有初始分词的相关度...")
-    word_scores = []
-
-    for word in all_words:
-        # 评估每个词的相关度
-        eval_result = await evaluate_query_relevance(word, context.q, None, context)
-        word_scores.append({
-            'word': word,
-            'score': eval_result.relevance_score,
-            'eval': eval_result
-        })
-        print(f"  {word}: {eval_result.relevance_score:.2f}")
-
-    # 按相关度排序,使用所有分词
-    word_scores.sort(key=lambda x: x['score'], reverse=True)
-    selected_words = word_scores  # 使用所有分词
-
-    # 将所有分词添加到演化图(全部被选中)
-    for item in word_scores:
-        is_selected = True  # 所有分词都被选中
-        query_state = QueryState(
-            query=item['word'],
-            level=1,
-            relevance_score=item['score'],
-            strategy="初始分词",
-            parent_query=context.q  # 父节点是原始问题
-        )
-
-        # 添加到演化图(会自动创建从parent_query到该query的边)
-        add_query_to_graph(context, query_state, 0, evaluation_reason=item['eval'].reason, is_selected=is_selected, parent_level=0)  # 父节点是根节点(level 0)
-
-        # 只有被选中的才加入队列
-        if is_selected:
-            query_queue.append(query_state)
-
-    print(f"\n初始query队列(按相关度排序): {[(q.query, f'{q.relevance_score:.2f}') for q in query_queue]}")
-    print(f"  (共评估了 {len(word_scores)} 个分词,全部加入队列)")
-
-    # 3. API实例
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-    xiaohongshu_search = XiaohongshuSearch()
-
-    # 4. 主循环
-    all_satisfied_notes = []
-    iteration = 0
-
-    while query_queue and iteration < max_iterations:
-        iteration += 1
-
-        # 获取当前层级(队列中最小的level)
-        current_level = min(q.level for q in query_queue)
-
-        # 提取当前层级的所有query
-        current_batch = [q for q in query_queue if q.level == current_level]
-        query_queue = [q for q in query_queue if q.level != current_level]
-
-        print(f"\n{'='*60}")
-        print(f"迭代 {iteration}: 处理第 {current_level} 层,共 {len(current_batch)} 个query")
-        print(f"{'='*60}")
-
-        # 记录本轮处理的queries
-        add_step(context, f"迭代 {iteration}", "iteration", {
-            "iteration": iteration,
-            "current_level": current_level,
-            "current_batch_size": len(current_batch),
-            "remaining_queue_size": len(query_queue),
-            "processing_queries": [{"query": q.query, "level": q.level} for q in current_batch]
-        })
-
-        new_queries_from_sug = []
-        new_queries_from_result = []
-
-        # 处理每个query
-        for query_state in current_batch:
-            print(f"\n处理Query [{query_state.level}]: {query_state.query} (分数: {query_state.relevance_score:.2f})")
-
-            # 检查终止条件
-            if query_state.is_terminated or query_state.no_suggestion_rounds >= 2:
-                print(f"  ✗ 已终止或连续2轮无suggestion,跳过该query")
-                query_state.is_terminated = True
-                continue
-
-            # 并行处理两个分支
-            sug_task = process_suggestions(
-                query_state.query, query_state, context.q, word_lib, context, xiaohongshu_api, iteration
-            )
-            result_task = process_search_results(
-                query_state.query, query_state, context.q, word_lib, context,
-                xiaohongshu_search, relevance_threshold, iteration
-            )
-
-            # 等待两个分支完成
-            sug_queries, (satisfied_notes, result_queries) = await asyncio.gather(
-                sug_task,
-                result_task
-            )
-
-            # 如果suggestion分支返回空,说明没有获取到suggestion,需要继承no_suggestion_rounds
-            # 注意:process_suggestions内部已经更新了query_state.no_suggestion_rounds
-            # 所以这里生成的新queries需要继承父query的no_suggestion_rounds(如果sug分支也返回空)
-            if not sug_queries and not result_queries:
-                # 两个分支都没有产生新query,标记当前query为终止
-                query_state.is_terminated = True
-                print(f"  ⚠ 两个分支均未产生新query,标记该query为终止")
-
-            new_queries_from_sug.extend(sug_queries)
-            new_queries_from_result.extend(result_queries)
-            all_satisfied_notes.extend(satisfied_notes)
-
-        # 更新队列
-        all_new_queries = new_queries_from_sug + new_queries_from_result
-
-        # 注意:不需要在这里再次添加到演化图,因为在 process_suggestions 和 process_search_results 中已经添加过了
-        # 如果在这里再次调用 add_query_to_graph,会覆盖之前设置的 evaluation_reason 等字段
-
-        query_queue.extend(all_new_queries)
-
-        # 去重(基于query文本)并过滤已终止的query
-        seen = set()
-        unique_queue = []
-        for q in query_queue:
-            if q.query not in seen and not q.is_terminated:
-                seen.add(q.query)
-                unique_queue.append(q)
-        query_queue = unique_queue
-
-        # 按相关度排序
-        query_queue.sort(key=lambda x: x.relevance_score, reverse=True)
-
-        print(f"\n本轮结果:")
-        print(f"  新增满足帖子: {len(satisfied_notes)}")
-        print(f"  累计满足帖子: {len(all_satisfied_notes)}")
-        print(f"  新增queries: {len(all_new_queries)}")
-        print(f"  队列剩余: {len(query_queue)}")
-
-        # 更新分词库到context
-        context.word_library = word_lib.model_dump()
-
-        # 如果满足条件的帖子足够多,可以提前结束
-        if len(all_satisfied_notes) >= 20:
-            print(f"\n已找到足够的满足帖子 ({len(all_satisfied_notes)}个),提前结束")
-            break
-
-    print(f"\n{'='*60}")
-    print(f"迭代搜索完成")
-    print(f"  总迭代次数: {iteration}")
-    print(f"  最终满足帖子数: {len(all_satisfied_notes)}")
-    print(f"  核心词库: {list(word_lib.core_words)}")
-    print(f"  最终分词库大小: {len(word_lib.words)}")
-    print(f"{'='*60}")
-
-    # 保存最终结果
-    add_step(context, "迭代搜索完成", "loop_complete", {
-        "total_iterations": iteration,
-        "total_satisfied_notes": len(all_satisfied_notes),
-        "core_words": list(word_lib.core_words),
-        "final_word_library_size": len(word_lib.words),
-        "final_word_library": list(word_lib.words)
-    })
-
-    return all_satisfied_notes
-
-
-# ============================================================================
-# 主函数
-# ============================================================================
-
-async def main(input_dir: str, max_iterations: int = 20, visualize: bool = False):
-    """主函数"""
-    current_time, log_url = set_trace()
-
-    # 读取输入
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 版本信息
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]
-
-    # 日志目录
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    # 创建运行上下文
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-
-    # 执行迭代搜索
-    satisfied_notes = await iterative_search_loop(
-        run_context,
-        max_iterations=max_iterations,
-        relevance_threshold=0.6
-    )
-
-    # 保存结果
-    run_context.satisfied_notes = satisfied_notes
-
-    # 格式化输出
-    output = f"原始问题:{run_context.q}\n"
-    output += f"找到满足需求的帖子:{len(satisfied_notes)} 个\n"
-    output += f"核心词库:{', '.join(run_context.word_library.get('core_words', []))}\n"
-    output += f"分词库大小:{len(run_context.word_library.get('words', []))} 个词\n"
-    output += "\n" + "="*60 + "\n"
-
-    if satisfied_notes:
-        output += "【满足需求的帖子】\n\n"
-        for idx, note in enumerate(satisfied_notes, 1):
-            output += f"{idx}. {note['title']}\n"
-            output += f"   相关度: {note['evaluation']['relevance_score']:.2f}\n"
-            output += f"   URL: {note['note_url']}\n\n"
-    else:
-        output += "未找到满足需求的帖子\n"
-
-    run_context.final_output = output
-
-    print(f"\n{'='*60}")
-    print("最终结果")
-    print(f"{'='*60}")
-    print(output)
-
-    # 保存日志
-    os.makedirs(run_context.log_dir, exist_ok=True)
-
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    context_dict = run_context.model_dump()
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(context_dict, f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-    steps_file_path = os.path.join(run_context.log_dir, "steps.json")
-    with open(steps_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.steps, f, ensure_ascii=False, indent=2)
-    print(f"Steps log saved to: {steps_file_path}")
-
-    # 保存Query演化图
-    query_graph_file_path = os.path.join(run_context.log_dir, "query_graph.json")
-    with open(query_graph_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.query_graph, f, ensure_ascii=False, indent=2)
-    print(f"Query graph saved to: {query_graph_file_path}")
-
-    # 可视化
-    if visualize:
-        import subprocess
-        output_html = os.path.join(run_context.log_dir, "visualization.html")
-        print(f"\n🎨 生成可视化HTML...")
-
-        # 获取绝对路径
-        vis_script = os.path.abspath("visualization/sug_v6_1_2_6/index.js")
-        abs_query_graph = os.path.abspath(query_graph_file_path)
-        abs_output_html = os.path.abspath(output_html)
-
-        # 在可视化脚本目录中执行,确保使用本地 node_modules
-        result = subprocess.run([
-            "node", "index.js",
-            abs_query_graph,
-            abs_output_html
-        ], cwd="visualization/sug_v6_1_2_6")
-
-        if result.returncode == 0:
-            print(f"✅ 可视化已生成: {output_html}")
-        else:
-            print(f"❌ 可视化生成失败")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具 - v6.1.2.5 迭代循环版")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    parser.add_argument(
-        "--max-iterations",
-        type=int,
-        default=20,
-        help="最大迭代次数,默认: 20"
-    )
-    parser.add_argument(
-        "--visualize",
-        action="store_true",
-        default=False,
-        help="运行完成后自动生成可视化HTML"
-    )
-    parser.add_argument(
-        "--visualize-only",
-        type=str,
-        help="仅生成可视化,指定query_graph.json文件路径"
-    )
-    args = parser.parse_args()
-
-    # 如果只是生成可视化
-    if args.visualize_only:
-        import subprocess
-        query_graph_path = args.visualize_only
-        output_html = os.path.splitext(query_graph_path)[0].replace("query_graph", "visualization") + ".html"
-        if not output_html.endswith(".html"):
-            output_html = os.path.join(os.path.dirname(query_graph_path), "visualization.html")
-
-        print(f"🎨 生成可视化HTML...")
-        print(f"输入: {query_graph_path}")
-        print(f"输出: {output_html}")
-
-        # 获取绝对路径
-        abs_query_graph = os.path.abspath(query_graph_path)
-        abs_output_html = os.path.abspath(output_html)
-
-        # 在可视化脚本目录中执行,确保使用本地 node_modules
-        result = subprocess.run([
-            "node", "index.js",
-            abs_query_graph,
-            abs_output_html
-        ], cwd="visualization/sug_v6_1_2_6")
-
-        if result.returncode == 0:
-            print(f"✅ 可视化已生成: {output_html}")
-        else:
-            print(f"❌ 可视化生成失败")
-        sys.exit(result.returncode)
-
-    asyncio.run(main(args.input_dir, max_iterations=args.max_iterations, visualize=args.visualize))

+ 0 - 1213
sug_v6_1_2_7.py

@@ -1,1213 +0,0 @@
-import asyncio
-import json
-import os
-import sys
-import argparse
-import time
-import re
-from datetime import datetime
-from typing import Literal, TypeVar, Type
-
-from agents import Agent, Runner
-from lib.my_trace import set_trace
-from pydantic import BaseModel, Field
-
-from lib.utils import read_file_as_string
-from lib.client import get_model
-MODEL_NAME = "google/gemini-2.5-flash"
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-from script.search.xiaohongshu_search import XiaohongshuSearch
-
-
-# ============================================================================
-# 数据模型
-# ============================================================================
-
-class Seg(BaseModel):
-    """分词结果"""
-    text: str
-    score_with_o: float
-    from_o: str
-
-
-class Word(BaseModel):
-    """词库中的词"""
-    text: str
-    score_with_o: float
-    from_o: str
-
-
-class Q(BaseModel):
-    """查询"""
-    text: str
-    score_with_o: float
-    from_source: str  # "seg" | "sug" | "add"
-
-
-class Sug(BaseModel):
-    """建议查询"""
-    text: str
-    score_with_o: float
-    from_q: dict  # {"text": str, "score_with_o": float}
-    evaluation_reason: str | None = None  # 评估理由
-
-
-class Seed(BaseModel):
-    """种子查询(用于加词探索)"""
-    text: str
-    added_words: list[str] = Field(default_factory=list)
-    from_type: str  # "seg" | "sug"
-
-
-class Post(BaseModel):
-    """帖子"""
-    note_id: str = ""
-    title: str = ""
-    body_text: str = ""
-    type: str = "normal"  # "video" | "normal"
-    images: list[str] = Field(default_factory=list)
-    video: str = ""
-    interact_info: dict = Field(default_factory=dict)
-    note_url: str = ""
-
-
-class Search(BaseModel):
-    """搜索结果(继承自Sug)"""
-    text: str
-    score_with_o: float
-    from_q: dict
-    post_list: list[Post] = Field(default_factory=list)
-
-
-class RunContext(BaseModel):
-    """运行上下文"""
-    version: str
-    input_files: dict[str, str]
-    c: str  # 原始需求(context)
-    o: str  # 原始问题
-    log_url: str
-    log_dir: str
-
-    # 核心数据
-    seg_list: list[dict] = Field(default_factory=list)
-    word_lists: dict[int, list[dict]] = Field(default_factory=dict)  # {round: word_list}
-    q_lists: dict[int, list[dict]] = Field(default_factory=dict)  # {round: q_list}
-    sug_list_lists: dict[int, list[list[dict]]] = Field(default_factory=dict)  # {round: [[sug, sug], [sug]]}
-    search_lists: dict[int, list[dict]] = Field(default_factory=dict)  # {round: search_list}
-    seed_lists: dict[int, list[dict]] = Field(default_factory=dict)  # {round: seed_list}
-
-    steps: list[dict] = Field(default_factory=list)
-
-    # 新增:详细的操作记录(中文命名,但数据结构保留英文)
-    轮次记录: dict[int, dict] = Field(default_factory=dict)
-
-    # 最终结果
-    all_posts: list[dict] = Field(default_factory=list)
-    final_output: str | None = None
-
-
-# ============================================================================
-# 辅助函数:记录操作
-# ============================================================================
-
-def init_round_record(run_context: RunContext, round_num: int, round_name: str):
-    """初始化一个轮次记录"""
-    run_context.轮次记录[round_num] = {
-        "轮次": round_num,
-        "名称": round_name,
-        "操作列表": []
-    }
-
-
-def add_operation_record(
-    run_context: RunContext,
-    round_num: int,
-    操作名称: str,
-    输入: dict,
-    处理过程: dict,
-    输出: dict
-):
-    """添加一条操作记录"""
-    from datetime import datetime
-
-    operation = {
-        "操作名称": 操作名称,
-        "轮次": round_num,
-        "时间": datetime.now().isoformat(),
-        "输入": 输入,
-        "处理过程": 处理过程,
-        "输出": 输出
-    }
-
-    if round_num not in run_context.轮次记录:
-        init_round_record(run_context, round_num, f"第{round_num}轮" if round_num > 0 else "初始化阶段")
-
-    run_context.轮次记录[round_num]["操作列表"].append(operation)
-
-
-def record_agent_call(
-    agent_name: str,
-    model: str,
-    instructions: str,
-    user_message: str,
-    raw_output: dict | str,
-    parsed: bool,
-    validation_error: str | None = None,
-    input_schema: dict | None = None
-) -> dict:
-    """记录单次Agent调用"""
-    return {
-        "Agent名称": agent_name,
-        "模型": model,
-        "系统提示词": instructions,
-        "输入Schema": input_schema,
-        "用户消息": user_message,
-        "原始输出": raw_output,
-        "解析成功": parsed,
-        "验证错误": validation_error
-    }
-
-
-# ============================================================================
-# JSON后处理:处理markdown包裹的JSON响应
-# ============================================================================
-
-def clean_json_response(text: str) -> str:
-    """清理可能包含markdown代码块包裹的JSON
-
-    模型可能返回:
-    ```json
-    {"key": "value"}
-    ```
-
-    需要清理为:
-    {"key": "value"}
-    """
-    text = text.strip()
-
-    # 移除开头的 ```json 或 ```
-    if text.startswith('```json'):
-        text = text[7:]
-    elif text.startswith('```'):
-        text = text[3:]
-
-    # 移除结尾的 ```
-    if text.endswith('```'):
-        text = text[:-3]
-
-    return text.strip()
-
-
-T = TypeVar('T', bound=BaseModel)
-
-async def run_agent_with_json_cleanup(
-    agent: Agent,
-    input_text: str,
-    output_type: Type[T]
-) -> T:
-    """运行Agent并处理可能的JSON包裹问题
-
-    如果Agent返回被markdown包裹的JSON,自动清理后重新解析
-    """
-    try:
-        result = await Runner.run(agent, input_text)
-        return result.final_output
-    except Exception as e:
-        error_msg = str(e)
-
-        # 检查是否是JSON解析错误
-        if "Invalid JSON when parsing" in error_msg:
-            # 尝试从错误消息中提取JSON
-            # 错误格式: "Invalid JSON when parsing ```json\n{...}\n``` for TypeAdapter(...)"
-            match = re.search(r'when parsing (.+?) for TypeAdapter', error_msg, re.DOTALL)
-            if match:
-                json_text = match.group(1)
-                cleaned_json = clean_json_response(json_text)
-                try:
-                    # 手动解析JSON并创建Pydantic对象
-                    parsed_data = json.loads(cleaned_json)
-                    return output_type(**parsed_data)
-                except Exception as parse_error:
-                    print(f"⚠️  JSON清理后仍无法解析: {parse_error}")
-                    print(f"   清理后的JSON: {cleaned_json}")
-                    raise ValueError(f"无法解析JSON: {parse_error}\n原始错误: {error_msg}")
-
-        # 如果不是JSON解析错误,或清理失败,重新抛出原始错误
-        raise
-
-
-# ============================================================================
-# Agent 定义
-# ============================================================================
-
-# Agent 1: 分词专家
-class WordSegmentation(BaseModel):
-    """分词结果"""
-    words: list[str] = Field(..., description="分词结果列表")
-    reasoning: str = Field(..., description="分词理由")
-
-word_segmentation_instructions = """
-你是分词专家。给定一个query,将其拆分成有意义的最小单元。
-
-## 分词原则
-1. 保留有搜索意义的词汇
-2. 拆分成独立的概念
-3. 保留专业术语的完整性
-4. 去除虚词(的、吗、呢等)
-
-## 输出要求
-返回分词列表和分词理由。
-
-IMPORTANT: 直接返回纯JSON对象,不要使用markdown代码块标记(不要用```json...```包裹)。
-""".strip()
-
-word_segmenter = Agent[None](
-    name="分词专家",
-    instructions=word_segmentation_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=WordSegmentation,
-)
-
-
-# Agent 2: Query相关度评估专家
-class RelevanceEvaluation(BaseModel):
-    """相关度评估"""
-    relevance_score: float = Field(..., description="相关性分数 0-1")
-    reason: str = Field(..., description="评估理由")
-
-relevance_evaluation_instructions = """
-你是Query相关度评估专家。
-
-## 任务
-评估当前query与原始问题的匹配程度。
-
-## 评估标准
-- 主题相关性
-- 要素覆盖度
-- 意图匹配度
-
-## 输出
-- relevance_score: 0-1的相关性分数
-- reason: 详细理由
-
-IMPORTANT: 直接返回纯JSON对象,不要使用markdown代码块标记(不要用```json...```包裹)。
-""".strip()
-
-relevance_evaluator = Agent[None](
-    name="Query相关度评估专家",
-    instructions=relevance_evaluation_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=RelevanceEvaluation,
-)
-
-
-# Agent 3: Word选择专家
-class WordSelection(BaseModel):
-    """Word选择结果"""
-    selected_word: str = Field(..., description="选中的词")
-    reasoning: str = Field(..., description="选择理由")
-
-word_selection_instructions = """
-你是Word选择专家。
-
-## 任务
-从候选词列表中选择一个最适合与当前seed组合的词,用于探索新的搜索query。
-
-## 选择原则
-1. 与seed的语义相关性
-2. 组合后的搜索价值
-3. 能拓展搜索范围
-
-## 输出
-返回选中的词和选择理由。
-""".strip()
-
-word_selector = Agent[None](
-    name="Word选择专家",
-    instructions=word_selection_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=WordSelection,
-)
-
-
-# Agent 4: 加词位置评估专家
-class WordInsertion(BaseModel):
-    """加词结果"""
-    new_query: str = Field(..., description="加词后的新query")
-    insertion_position: str = Field(..., description="插入位置描述")
-    reasoning: str = Field(..., description="插入理由")
-
-word_insertion_instructions = """
-你是加词位置评估专家。
-
-## 任务
-将新词加到当前query的最合适位置,保持语义通顺。
-
-## 原则
-1. 保持语法正确
-2. 语义连贯
-3. 符合搜索习惯
-
-## 输出
-返回新query、插入位置描述和理由。
-""".strip()
-
-word_inserter = Agent[None](
-    name="加词位置评估专家",
-    instructions=word_insertion_instructions,
-    model=get_model(MODEL_NAME),
-    output_type=WordInsertion,
-)
-
-
-# ============================================================================
-# 辅助函数
-# ============================================================================
-
-def add_step(context: RunContext, step_name: str, step_type: str, data: dict):
-    """添加步骤记录"""
-    step = {
-        "step_number": len(context.steps) + 1,
-        "step_name": step_name,
-        "step_type": step_type,
-        "timestamp": datetime.now().isoformat(),
-        "data": data
-    }
-    context.steps.append(step)
-    return step
-
-
-def process_note_data(note: dict) -> Post:
-    """处理搜索接口返回的帖子数据,转换为Post对象"""
-    note_card = note.get("note_card", {})
-    image_list = note_card.get("image_list", [])
-    interact_info = note_card.get("interact_info", {})
-
-    # 提取图片URLs - 使用 image_url 字段
-    images = []
-    for img in image_list:
-        if "image_url" in img:
-            images.append(img["image_url"])
-
-    # 判断是否是视频
-    note_type = note_card.get("type", "normal")
-    video_url = ""
-    if note_type == "video":
-        # 视频类型可能有不同的结构,这里先留空
-        # 如果需要可以后续补充
-        pass
-
-    return Post(
-        note_id=note.get("id") or "",
-        title=note_card.get("display_title") or "",
-        body_text=note_card.get("desc") or "",
-        type=note_type,
-        images=images,
-        video=video_url,
-        interact_info={
-            "liked_count": interact_info.get("liked_count", 0),
-            "collected_count": interact_info.get("collected_count", 0),
-            "comment_count": interact_info.get("comment_count", 0),
-            "shared_count": interact_info.get("shared_count", 0)
-        },
-        note_url=f"https://www.xiaohongshu.com/explore/{note.get('id') or ''}"
-    )
-
-
-# ============================================================================
-# 核心流程函数
-# ============================================================================
-
-async def evaluate_query_with_o(query_text: str, original_o: str) -> tuple[float, str]:
-    """评估query与原始问题o的相关度
-
-    Returns:
-        (score, reason)
-    """
-    eval_input = f"""
-<原始问题>
-{original_o}
-</原始问题>
-
-<当前Query>
-{query_text}
-</当前Query>
-
-请评估当前query与原始问题的相关度。
-"""
-
-    evaluation = await run_agent_with_json_cleanup(
-        relevance_evaluator,
-        eval_input,
-        RelevanceEvaluation
-    )
-
-    return evaluation.relevance_score, evaluation.reason
-
-
-async def initialize(context: RunContext):
-    """初始化:分词 → seg_list → word_list_1, q_list_1, seed_list_1"""
-
-    print("\n" + "="*60)
-    print("初始化阶段")
-    print("="*60)
-
-    # 初始化轮次0
-    init_round_record(context, 0, "初始化阶段")
-
-    # 1. 分词
-    print(f"\n[1/4] 分词原始问题: {context.o}")
-    segmentation = await run_agent_with_json_cleanup(
-        word_segmenter,
-        context.o,
-        WordSegmentation
-    )
-
-    print(f"  分词结果: {segmentation.words}")
-    print(f"  分词理由: {segmentation.reasoning}")
-
-    # 2. 分词评估(并发)
-    print(f"\n[2/4] 评估每个seg与原始问题的相关度...")
-    seg_list = []
-    agent_calls_seg_eval = []
-
-    # 并发评估所有分词
-    eval_tasks = [evaluate_query_with_o(word, context.o) for word in segmentation.words]
-    eval_results = await asyncio.gather(*eval_tasks)
-
-    for word, (score, reason) in zip(segmentation.words, eval_results):
-        seg = Seg(text=word, score_with_o=score, from_o=context.o)
-        seg_list.append(seg.model_dump())
-        print(f"  {word}: {score:.2f}")
-
-        # 记录每个seg的评估
-        agent_calls_seg_eval.append(
-            record_agent_call(
-                agent_name="Query相关度评估专家",
-                model=MODEL_NAME,
-                instructions=relevance_evaluation_instructions,
-                user_message=f"评估query与原始问题的相关度:\n\nQuery: {word}\n原始问题: {context.o}",
-                raw_output={"score": score, "reason": reason},
-                parsed=True
-            )
-        )
-
-    context.seg_list = seg_list
-
-    # 记录分词操作
-    add_operation_record(
-        context,
-        round_num=0,
-        操作名称="分词",
-        输入={"原始问题": context.o},
-        处理过程={
-            "Agent调用": record_agent_call(
-                agent_name="分词专家",
-                model=MODEL_NAME,
-                instructions=word_segmentation_instructions,
-                user_message=f"请对以下query进行分词:{context.o}",
-                raw_output={"words": segmentation.words, "reasoning": segmentation.reasoning},
-                parsed=True,
-                input_schema={"type": "WordSegmentation", "fields": {"words": "list[str]", "reasoning": "str"}}
-            ),
-            "seg评估Agent调用列表": agent_calls_seg_eval
-        },
-        输出={"seg_list": seg_list}
-    )
-
-    # 3. 构建 word_list_1(直接从seg_list复制)
-    print(f"\n[3/4] 构建 word_list_1...")
-    word_list_1 = []
-    for seg in seg_list:
-        word = Word(text=seg["text"], score_with_o=seg["score_with_o"], from_o=seg["from_o"])
-        word_list_1.append(word.model_dump())
-
-    context.word_lists[1] = word_list_1
-    print(f"  word_list_1 大小: {len(word_list_1)}")
-
-    # 4. 构建 q_list_1 和 seed_list_1
-    print(f"\n[4/4] 构建 q_list_1 和 seed_list_1...")
-    q_list_1 = []
-    seed_list_1 = []
-
-    for seg in seg_list:
-        # q_list_1: seg作为q
-        q = Q(text=seg["text"], score_with_o=seg["score_with_o"], from_source="seg")
-        q_list_1.append(q.model_dump())
-
-        # seed_list_1: seg作为seed
-        seed = Seed(text=seg["text"], added_words=[], from_type="seg")
-        seed_list_1.append(seed.model_dump())
-
-    context.q_lists[1] = q_list_1
-    context.seed_lists[1] = seed_list_1
-
-    print(f"  q_list_1 大小: {len(q_list_1)}")
-    print(f"  seed_list_1 大小: {len(seed_list_1)}")
-
-    # 记录初始化操作
-    add_operation_record(
-        context,
-        round_num=0,
-        操作名称="初始化",
-        输入={"seg_list": seg_list},
-        处理过程={"说明": "从seg_list构建初始q_list和seed_list"},
-        输出={
-            "word_list_1": word_list_1,
-            "q_list_1": q_list_1,
-            "seed_list_1": seed_list_1
-        }
-    )
-
-    add_step(context, "初始化完成", "initialize", {
-        "seg_count": len(seg_list),
-        "word_list_1_count": len(word_list_1),
-        "q_list_1_count": len(q_list_1),
-        "seed_list_1_count": len(seed_list_1)
-    })
-
-
-async def process_round(round_num: int, context: RunContext, xiaohongshu_api: XiaohongshuSearchRecommendations, xiaohongshu_search: XiaohongshuSearch, sug_threshold: float = 0.7):
-    """处理一轮迭代
-
-    Args:
-        round_num: 当前轮数
-        context: 运行上下文
-        xiaohongshu_api: sug API
-        xiaohongshu_search: search API
-        sug_threshold: sug评分阈值(默认0.7)
-    """
-
-    print(f"\n" + "="*60)
-    print(f"第 {round_num} 轮")
-    print("="*60)
-
-    # 初始化轮次记录
-    init_round_record(context, round_num, f"第{round_num}轮迭代")
-
-    q_list_n = context.q_lists.get(round_num, [])
-    if not q_list_n:
-        print(f"  q_list_{round_num} 为空,跳过本轮")
-        return
-
-    print(f"  处理 {len(q_list_n)} 个query")
-
-    # 1. 请求sug
-    print(f"\n[1/5] 请求sug...")
-    sug_list_list_n = []
-    api_calls_detail = []
-
-    for q_data in q_list_n:
-        q_text = q_data["text"]
-        suggestions = xiaohongshu_api.get_recommendations(keyword=q_text)
-
-        if not suggestions:
-            print(f"  {q_text}: 无sug")
-            sug_list_list_n.append([])
-            api_calls_detail.append({
-                "query": q_text,
-                "sug_count": 0
-            })
-            continue
-
-        print(f"  {q_text}: 获取 {len(suggestions)} 个sug")
-        sug_list_list_n.append(suggestions)
-        api_calls_detail.append({
-            "query": q_text,
-            "sug_count": len(suggestions)
-        })
-
-    # 记录请求sug操作
-    total_sugs = sum(len(sl) for sl in sug_list_list_n)
-    add_operation_record(
-        context,
-        round_num=round_num,
-        操作名称="请求推荐词",
-        输入={"q_list": [{"text": q["text"], "score": q["score_with_o"]} for q in q_list_n]},
-        处理过程={"API调用": api_calls_detail},
-        输出={
-            "sug_list_list": [[{"text": s, "from_q": q_list_n[i]["text"]} for s in sl] for i, sl in enumerate(sug_list_list_n)],
-            "总推荐词数": total_sugs
-        }
-    )
-
-    # 2. sug评估(批量并发,限制并发数为10)
-    print(f"\n[2/5] 评估sug...")
-    sug_list_list_evaluated = []
-
-    # 收集所有需要评估的sug及其上下文
-    all_sug_tasks = []
-    sug_contexts = []  # 记录每个sug对应的q_data和位置
-
-    for i, sug_list in enumerate(sug_list_list_n):
-        q_data = q_list_n[i]
-        for sug_text in sug_list:
-            all_sug_tasks.append(evaluate_query_with_o(sug_text, context.o))
-            sug_contexts.append((i, q_data, sug_text))
-
-    # 批量并发评估(每批10个)
-    batch_size = 10
-    all_results = []
-    batches_detail = []
-
-    for batch_idx in range(0, len(all_sug_tasks), batch_size):
-        batch_tasks = all_sug_tasks[batch_idx:batch_idx+batch_size]
-        batch_results = await asyncio.gather(*batch_tasks)
-        all_results.extend(batch_results)
-
-        # 记录这个批次的Agent调用
-        batch_agent_calls = []
-        start_idx = batch_idx
-        for j, (score, reason) in enumerate(batch_results):
-            if start_idx + j < len(sug_contexts):
-                _, _, sug_text = sug_contexts[start_idx + j]
-                batch_agent_calls.append(
-                    record_agent_call(
-                        agent_name="Query相关度评估专家",
-                        model=MODEL_NAME,
-                        instructions=relevance_evaluation_instructions,
-                        user_message=f"评估query与原始问题的相关度:\n\nQuery: {sug_text}\n原始问题: {context.o}",
-                        raw_output={"score": score, "reason": reason},
-                        parsed=True
-                    )
-                )
-
-        batches_detail.append({
-            "批次ID": len(batches_detail),
-            "并发执行": True,
-            "Agent调用列表": batch_agent_calls
-        })
-
-    # 组织结果
-    result_index = 0
-    current_list_index = -1
-    evaluated_sugs = []
-
-    for list_idx, q_data, sug_text in sug_contexts:
-        if list_idx != current_list_index:
-            if evaluated_sugs:
-                sug_list_list_evaluated.append(evaluated_sugs)
-            evaluated_sugs = []
-            current_list_index = list_idx
-
-        score, reason = all_results[result_index]
-        result_index += 1
-
-        sug = Sug(
-            text=sug_text,
-            score_with_o=score,
-            from_q={"text": q_data["text"], "score_with_o": q_data["score_with_o"]},
-            evaluation_reason=reason
-        )
-        evaluated_sugs.append(sug.model_dump())
-        print(f"    {sug_text}: {score:.2f}")
-
-    # 添加最后一批
-    if evaluated_sugs:
-        sug_list_list_evaluated.append(evaluated_sugs)
-
-    context.sug_list_lists[round_num] = sug_list_list_evaluated
-
-    # 记录评估sug操作
-    add_operation_record(
-        context,
-        round_num=round_num,
-        操作名称="评估推荐词",
-        输入={
-            "待评估推荐词": [[s for s in sl] for sl in sug_list_list_n],
-            "总数": len(all_sug_tasks)
-        },
-        处理过程={"批次列表": batches_detail},
-        输出={"已评估推荐词": sug_list_list_evaluated}
-    )
-
-    # 3. 构建search_list_n(阈值>= 0.7的sug)
-    print(f"\n[3/5] 构建search_list并执行搜索...")
-    search_list_n = []
-    filter_comparisons = []
-    search_details = []
-
-    for sug_list_evaluated in sug_list_list_evaluated:
-        for sug_data in sug_list_evaluated:
-            # 记录筛选比较
-            passed = sug_data["score_with_o"] >= sug_threshold
-            filter_comparisons.append({
-                "文本": sug_data["text"],
-                "分数": sug_data["score_with_o"],
-                "阈值": sug_threshold,
-                "通过": passed
-            })
-
-            if passed:
-                print(f"  搜索: {sug_data['text']} (分数: {sug_data['score_with_o']:.2f})")
-
-                try:
-                    # 执行搜索
-                    search_result = xiaohongshu_search.search(keyword=sug_data["text"])
-                    result_str = search_result.get("result", "{}")
-                    if isinstance(result_str, str):
-                        result_data = json.loads(result_str)
-                    else:
-                        result_data = result_str
-
-                    notes = result_data.get("data", {}).get("data", [])
-                    print(f"    → 搜索到 {len(notes)} 个帖子")
-
-                    # 转换为Post对象
-                    post_list = []
-                    for note in notes:
-                        post = process_note_data(note)
-                        post_list.append(post.model_dump())
-                        context.all_posts.append(post.model_dump())
-
-                    # 创建Search对象
-                    search = Search(
-                        text=sug_data["text"],
-                        score_with_o=sug_data["score_with_o"],
-                        from_q=sug_data["from_q"],
-                        post_list=post_list
-                    )
-                    search_list_n.append(search.model_dump())
-
-                    # 记录搜索详情
-                    search_details.append({
-                        "查询": sug_data["text"],
-                        "分数": sug_data["score_with_o"],
-                        "成功": True,
-                        "帖子数量": len(post_list),
-                        "错误": None
-                    })
-
-                except Exception as e:
-                    print(f"    ✗ 搜索失败: {e}")
-                    search_details.append({
-                        "查询": sug_data["text"],
-                        "分数": sug_data["score_with_o"],
-                        "成功": False,
-                        "帖子数量": 0,
-                        "错误": str(e)
-                    })
-
-    context.search_lists[round_num] = search_list_n
-    print(f"  本轮搜索到 {len(search_list_n)} 个有效结果")
-
-    # 记录构建search和执行搜索操作(合并为一个操作)
-    total_posts = sum(len(s["post_list"]) for s in search_list_n)
-    add_operation_record(
-        context,
-        round_num=round_num,
-        操作名称="筛选并执行搜索",
-        输入={"已评估推荐词": sug_list_list_evaluated},
-        处理过程={
-            "筛选条件": f"分数 >= {sug_threshold}",
-            "筛选比较": filter_comparisons,
-            "搜索详情": search_details
-        },
-        输出={
-            "search_list": search_list_n,
-            "成功搜索数": len(search_list_n),
-            "总帖子数": total_posts
-        }
-    )
-
-    # 4. 构建word_list_(n+1)(先直接复制)
-    print(f"\n[4/5] 构建word_list_{round_num+1}...")
-    word_list_n = context.word_lists.get(round_num, [])
-    word_list_next = word_list_n.copy()
-    context.word_lists[round_num + 1] = word_list_next
-    print(f"  word_list_{round_num+1} 大小: {len(word_list_next)}")
-
-    # 5. 构建q_list_(n+1)和更新seed_list
-    print(f"\n[5/5] 构建q_list_{round_num+1}和更新seed_list...")
-    q_list_next = []
-    seed_list_n = context.seed_lists.get(round_num, [])
-    seed_list_next = seed_list_n.copy()
-
-    # 5.1 从seed加词(串行处理,避免重复)
-    print(f"  [5.1] 从seed加词生成新q(串行处理,去重)...")
-
-    add_word_attempts = []  # 记录所有尝试
-    new_queries_from_add = []
-    generated_query_texts = set()  # 记录已生成的查询文本
-
-    for seed_data in seed_list_n:
-        seed_text = seed_data["text"]
-        added_words = seed_data["added_words"]
-
-        # 过滤出未使用的词
-        candidate_words = []
-        for word_data in word_list_next:
-            word_text = word_data["text"]
-            # 简单字符串过滤
-            if word_text not in seed_text and word_text not in added_words:
-                candidate_words.append(word_data)
-
-        if not candidate_words:
-            print(f"    {seed_text}: 无可用词")
-            continue
-
-        attempt = {
-            "种子": {"text": seed_text, "已添加词": added_words},
-            "候选词": [w["text"] for w in candidate_words[:10]]
-        }
-
-        # 使用agent选择词(提供已生成的查询列表)
-        already_generated_str = ""
-        if generated_query_texts:
-            already_generated_str = f"""
-<已生成的查询>
-{', '.join(sorted(generated_query_texts))}
-</已生成的查询>
-
-注意:请避免生成与上述已存在的查询重复或过于相似的新查询。
-"""
-
-        selection_input = f"""
-<当前Seed>
-{seed_text}
-</当前Seed>
-
-<候选词列表>
-{', '.join([w['text'] for w in candidate_words[:10]])}
-</候选词列表>
-{already_generated_str}
-请从候选词中选择一个最适合与seed组合的词。
-"""
-        selection = await run_agent_with_json_cleanup(
-            word_selector,
-            selection_input,
-            WordSelection
-        )
-        selected_word = selection.selected_word
-
-        # 确保选中的词在候选列表中
-        if selected_word not in [w["text"] for w in candidate_words]:
-            # 如果agent选择的词不在候选列表中,使用第一个候选词
-            selected_word = candidate_words[0]["text"]
-
-        # 记录选词
-        attempt["步骤1_选词"] = record_agent_call(
-            agent_name="Word选择专家",
-            model=MODEL_NAME,
-            instructions=word_selection_instructions,
-            user_message=selection_input,
-            raw_output={"selected_word": selection.selected_word, "reasoning": selection.reasoning},
-            parsed=True,
-            input_schema={"type": "WordSelection", "fields": {"selected_word": "str", "reasoning": "str"}}
-        )
-
-        # 使用加词agent
-        insertion_input = f"""
-<当前Query>
-{seed_text}
-</当前Query>
-
-<要添加的词>
-{selected_word}
-</要添加的词>
-
-请将这个词加到query的最合适位置。
-"""
-        insertion = await run_agent_with_json_cleanup(
-            word_inserter,
-            insertion_input,
-            WordInsertion
-        )
-        new_query_text = insertion.new_query
-
-        # 记录插入位置
-        attempt["步骤2_插入位置"] = record_agent_call(
-            agent_name="加词位置评估专家",
-            model=MODEL_NAME,
-            instructions=word_insertion_instructions,
-            user_message=insertion_input,
-            raw_output={"new_query": insertion.new_query, "reasoning": insertion.reasoning},
-            parsed=True,
-            input_schema={"type": "WordInsertion", "fields": {"new_query": "str", "reasoning": "str"}}
-        )
-
-        # 检查是否重复
-        if new_query_text in generated_query_texts:
-            print(f"    {seed_text} + {selected_word} → {new_query_text} (重复,跳过)")
-            attempt["跳过原因"] = "查询重复"
-            add_word_attempts.append(attempt)
-            continue
-
-        # 立即评估新query
-        score, reason = await evaluate_query_with_o(new_query_text, context.o)
-
-        # 记录评估
-        attempt["步骤3_评估新查询"] = record_agent_call(
-            agent_name="Query相关度评估专家",
-            model=MODEL_NAME,
-            instructions=relevance_evaluation_instructions,
-            user_message=f"评估新query的相关度:\n\nQuery: {new_query_text}\n原始问题: {context.o}",
-            raw_output={"score": score, "reason": reason},
-            parsed=True
-        )
-        add_word_attempts.append(attempt)
-
-        # 创建新q并加入列表
-        new_q = Q(text=new_query_text, score_with_o=score, from_source="add")
-        q_list_next.append(new_q.model_dump())
-        new_queries_from_add.append(new_q.model_dump())
-        generated_query_texts.add(new_query_text)
-
-        # 更新seed的added_words
-        for seed in seed_list_next:
-            if seed["text"] == seed_text:
-                seed["added_words"].append(selected_word)
-                break
-
-        print(f"    {seed_text} + {selected_word} → {new_query_text} (分数: {score:.2f})")
-
-    # 记录加词操作
-    add_operation_record(
-        context,
-        round_num=round_num,
-        操作名称="加词生成新查询",
-        输入={
-            "seed_list": seed_list_n,
-            "word_list": word_list_next
-        },
-        处理过程={"尝试列表": add_word_attempts},
-        输出={"新查询列表": new_queries_from_add}
-    )
-
-    # 5.2 从sug加入q_list(条件:sug分数 > from_q分数)
-    print(f"  [5.2] 从sug加入q_list_{round_num+1}(条件:sug分数 > from_q分数)...")
-    sug_added_count = 0
-    sug_filter_comparisons = []
-    selected_sugs = []
-
-    for sug_list_evaluated in sug_list_list_evaluated:
-        for sug_data in sug_list_evaluated:
-            # 新条件:sug的分数 > 其来源query的分数
-            from_q_score = sug_data["from_q"]["score_with_o"]
-            passed = sug_data["score_with_o"] > from_q_score
-
-            sug_filter_comparisons.append({
-                "推荐词": sug_data["text"],
-                "推荐词分数": sug_data["score_with_o"],
-                "来源查询分数": from_q_score,
-                "通过": passed,
-                "原因": f"{sug_data['score_with_o']:.2f} > {from_q_score:.2f}" if passed else f"{sug_data['score_with_o']:.2f} <= {from_q_score:.2f}"
-            })
-
-            if passed:
-                # 检查是否已存在
-                if sug_data["text"] not in [q["text"] for q in q_list_next]:
-                    new_q = Q(text=sug_data["text"], score_with_o=sug_data["score_with_o"], from_source="sug")
-                    q_list_next.append(new_q.model_dump())
-                    selected_sugs.append(new_q.model_dump())
-                    sug_added_count += 1
-                    print(f"    ✓ {sug_data['text']} ({sug_data['score_with_o']:.2f} > {from_q_score:.2f})")
-
-    print(f"    添加 {sug_added_count} 个sug到q_list_{round_num+1}")
-
-    # 记录筛选sug操作
-    add_operation_record(
-        context,
-        round_num=round_num,
-        操作名称="筛选推荐词进入下轮",
-        输入={"已评估推荐词": sug_list_list_evaluated},
-        处理过程={
-            "筛选条件": "推荐词分数 > 来源查询分数",
-            "比较结果": sug_filter_comparisons
-        },
-        输出={"选中推荐词": selected_sugs}
-    )
-
-    # 5.3 更新seed_list(从sug中添加新seed,条件:sug分数 > from_q分数)
-    print(f"  [5.3] 更新seed_list_{round_num+1}(条件:sug分数 > from_q分数)...")
-    seed_texts_existing = [s["text"] for s in seed_list_next]
-    new_seed_count = 0
-
-    for sug_list_evaluated in sug_list_list_evaluated:
-        for sug_data in sug_list_evaluated:
-            from_q_score = sug_data["from_q"]["score_with_o"]
-            # 新条件:sug的分数 > 其来源query的分数
-            if sug_data["score_with_o"] > from_q_score and sug_data["text"] not in seed_texts_existing:
-                new_seed = Seed(text=sug_data["text"], added_words=[], from_type="sug")
-                seed_list_next.append(new_seed.model_dump())
-                seed_texts_existing.append(sug_data["text"])
-                new_seed_count += 1
-
-    print(f"    添加 {new_seed_count} 个sug到seed_list_{round_num+1}")
-
-    context.q_lists[round_num + 1] = q_list_next
-    context.seed_lists[round_num + 1] = seed_list_next
-
-    print(f"\n  q_list_{round_num+1} 大小: {len(q_list_next)}")
-    print(f"  seed_list_{round_num+1} 大小: {len(seed_list_next)}")
-
-    # 记录构建下一轮操作
-    add_operation_record(
-        context,
-        round_num=round_num,
-        操作名称="构建下一轮",
-        输入={
-            "加词新查询": new_queries_from_add,
-            "选中推荐词": selected_sugs
-        },
-        处理过程={
-            "合并": {
-                "来自加词": len(new_queries_from_add),
-                "来自推荐词": len(selected_sugs),
-                "合并前总数": len(new_queries_from_add) + len(selected_sugs)
-            },
-            "去重": {
-                "唯一数": len(q_list_next)
-            }
-        },
-        输出={
-            "下轮查询列表": q_list_next,
-            "下轮种子列表": seed_list_next
-        }
-    )
-
-    add_step(context, f"第{round_num}轮完成", "round", {
-        "round": round_num,
-        "q_list_count": len(q_list_n),
-        "sug_total_count": sum(len(s) for s in sug_list_list_evaluated),
-        "search_count": len(search_list_n),
-        "posts_found": sum(len(s["post_list"]) for s in search_list_n),
-        "q_list_next_count": len(q_list_next),
-        "seed_list_next_count": len(seed_list_next)
-    })
-
-
-async def main_loop(context: RunContext, max_rounds: int = 2):
-    """主循环
-
-    Args:
-        context: 运行上下文
-        max_rounds: 最大轮数(默认2)
-    """
-
-    print("\n" + "="*60)
-    print("开始主循环")
-    print("="*60)
-
-    # 初始化
-    await initialize(context)
-
-    # API实例
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-    xiaohongshu_search = XiaohongshuSearch()
-
-    # 迭代
-    for round_num in range(1, max_rounds + 1):
-        await process_round(round_num, context, xiaohongshu_api, xiaohongshu_search)
-
-        # 检查终止条件
-        q_list_next = context.q_lists.get(round_num + 1, [])
-        if not q_list_next:
-            print(f"\n  q_list_{round_num + 1} 为空,提前结束")
-            break
-
-    print("\n" + "="*60)
-    print("主循环完成")
-    print("="*60)
-    print(f"  总共收集 {len(context.all_posts)} 个帖子")
-
-
-# ============================================================================
-# 主函数
-# ============================================================================
-
-async def main(input_dir: str, max_rounds: int = 2, visualize: bool = False):
-    """主函数"""
-    current_time, log_url = set_trace()
-
-    # 读取输入
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    c = read_file_as_string(input_context_file)
-    o = read_file_as_string(input_q_file)
-
-    # 版本信息
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]
-
-    # 日志目录
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    # 创建运行上下文
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        c=c,
-        o=o,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-
-    # 执行主循环
-    await main_loop(run_context, max_rounds=max_rounds)
-
-    # 格式化输出
-    output = f"原始需求:{run_context.c}\n"
-    output += f"原始问题:{run_context.o}\n"
-    output += f"收集帖子:{len(run_context.all_posts)} 个\n"
-    output += "\n" + "="*60 + "\n"
-
-    if run_context.all_posts:
-        output += "【收集到的帖子】\n\n"
-        for idx, post in enumerate(run_context.all_posts[:20], 1):  # 只显示前20个
-            output += f"{idx}. {post['title']}\n"
-            output += f"   类型: {post['type']}\n"
-            output += f"   URL: {post['note_url']}\n\n"
-    else:
-        output += "未收集到帖子\n"
-
-    run_context.final_output = output
-
-    print(f"\n{'='*60}")
-    print("最终结果")
-    print(f"{'='*60}")
-    print(output)
-
-    # 保存日志
-    os.makedirs(run_context.log_dir, exist_ok=True)
-
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    context_dict = run_context.model_dump()
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(context_dict, f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-    steps_file_path = os.path.join(run_context.log_dir, "steps.json")
-    with open(steps_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.steps, f, ensure_ascii=False, indent=2)
-    print(f"Steps log saved to: {steps_file_path}")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具 - v6.1.2.7 基于seed的迭代版")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    parser.add_argument(
-        "--max-rounds",
-        type=int,
-        default=2,
-        help="最大轮数,默认: 2"
-    )
-    parser.add_argument(
-        "--visualize",
-        action="store_true",
-        default=True,
-        help="运行完成后自动生成可视化HTML"
-    )
-    args = parser.parse_args()
-
-    asyncio.run(main(args.input_dir, max_rounds=args.max_rounds, visualize=args.visualize))

+ 990 - 0
sug_v6_1_2_8_流程分析.md

@@ -0,0 +1,990 @@
+# sug_v6_1_2_8.py 流程分析文档
+
+## 📋 概述
+
+`sug_v6_1_2_8.py` 是一个基于 LLM Agent 的智能搜索查询优化工具,主要用于小红书平台的搜索优化。通过多轮迭代的方式,从原始查询出发,逐步扩展和优化搜索词,最终获取高质量的搜索结果。
+
+**版本**: v6.1.2.8
+**核心模型**: google/gemini-2.5-flash
+**主要特性**:
+- 🔄 多轮迭代优化
+- 🤖 多 Agent 协作
+- 📊 相关度评分系统
+- 🔍 小红书搜索集成
+- 📈 可视化支持
+
+---
+
+## 🏗️ 整体架构
+
+### 架构图
+
+```
+原始问题(o)
+    ↓
+[初始化阶段]
+    ├─ 分词 → seg_list
+    ├─ 评估分词相关度
+    ├─ 构建 word_list_1
+    ├─ 构建 q_list_1
+    └─ 构建 seed_list
+    ↓
+[第1轮迭代]
+    ├─ 请求 sug (建议词)
+    ├─ 评估 sug 相关度
+    ├─ 构建 search_list (高分sug搜索)
+    ├─ 为 seed 加词 → q_list_next
+    ├─ 更新 seed_list
+    └─ 保存搜索结果
+    ↓
+[第2轮迭代] ...
+    ↓
+[第N轮迭代] ...
+    ↓
+[输出结果 + 可视化]
+```
+
+### 核心组件
+
+1. **数据模型层** - 定义所有数据结构(Seg, Word, Q, Sug, Seed, Post, Search)
+2. **Agent 层** - 三个专家 Agent(分词、相关度评估、加词选择)
+3. **流程控制层** - 初始化、轮次迭代、主循环
+4. **外部服务层** - 小红书 API 集成(搜索推荐、搜索)
+
+---
+
+## 📦 数据模型
+
+### 核心数据结构
+
+#### 1. Seg (分词)
+```python
+class Seg(BaseModel):
+    text: str                    # 分词文本
+    score_with_o: float = 0.0    # 与原始问题的评分
+    reason: str = ""             # 评分理由
+    from_o: str = ""             # 原始问题
+```
+
+**用途**: 存储原始问题分词后的每个词单元
+
+#### 2. Word (词)
+```python
+class Word(BaseModel):
+    text: str                    # 词文本
+    score_with_o: float = 0.0    # 与原始问题的评分
+    from_o: str = ""             # 原始问题
+```
+
+**用途**: 词库,用于后续组合新的查询词
+
+#### 3. Q (查询)
+```python
+class Q(BaseModel):
+    text: str                    # 查询文本
+    score_with_o: float = 0.0    # 与原始问题的评分
+    reason: str = ""             # 评分理由
+    from_source: str = ""        # 来源: seg/sug/add
+```
+
+**用途**: 待处理的查询队列,每轮从 q_list 中取 query 进行处理
+
+#### 4. Sug (建议词)
+```python
+class Sug(BaseModel):
+    text: str                    # 建议词文本
+    score_with_o: float = 0.0    # 与原始问题的评分
+    reason: str = ""             # 评分理由
+    from_q: QFromQ | None        # 来自哪个 q
+```
+
+**用途**: 存储从小红书 API 获取的建议词
+
+#### 5. Seed (种子)
+```python
+class Seed(BaseModel):
+    text: str                    # 种子文本
+    added_words: list[str]       # 已添加的词
+    from_type: str = ""          # 来源: seg/sug
+    score_with_o: float = 0.0    # 与原始问题的评分
+```
+
+**用途**: 用于加词扩展的基础词,记录已经添加过的词以避免重复
+
+#### 6. Post (帖子)
+```python
+class Post(BaseModel):
+    title: str                   # 标题
+    body_text: str               # 正文
+    type: str = "normal"         # 类型: video/normal
+    images: list[str]            # 图片URL列表
+    video: str = ""              # 视频URL
+    interact_info: dict          # 互动信息(点赞/收藏/评论/分享)
+    note_id: str                 # 笔记ID
+    note_url: str                # 笔记URL
+```
+
+**用途**: 存储小红书搜索结果的帖子详情
+
+#### 7. Search (搜索结果)
+```python
+class Search(Sug):
+    post_list: list[Post]        # 搜索到的帖子列表
+```
+
+**用途**: 继承 Sug,附加实际搜索到的帖子数据
+
+#### 8. RunContext (运行上下文)
+```python
+class RunContext(BaseModel):
+    version: str                 # 版本号
+    input_files: dict            # 输入文件路径
+    c: str                       # 原始需求
+    o: str                       # 原始问题
+    log_url: str                 # 日志URL
+    log_dir: str                 # 日志目录
+    rounds: list[dict]           # 每轮的详细数据
+    final_output: str | None     # 最终结果
+```
+
+**用途**: 记录整个运行过程的上下文信息和中间结果
+
+---
+
+## 🤖 Agent 系统
+
+### Agent 1: 分词专家 (word_segmenter)
+
+**功能**: 将原始问题拆分成有意义的最小单元
+
+**输入**: 原始查询文本
+**输出**:
+```python
+class WordSegmentation:
+    words: list[str]        # 分词结果列表
+    reasoning: str          # 分词理由
+```
+
+**分词原则**:
+1. 保留有搜索意义的词汇
+2. 拆分成独立的概念
+3. 保留专业术语的完整性
+4. 去除虚词(的、吗、呢等)
+
+**示例**:
+- 输入: "如何获取能体现川西秋季特色的高质量风光摄影素材?"
+- 输出: ["川西", "秋季", "风光摄影", "素材"]
+
+### Agent 2: 相关度评估专家 (relevance_evaluator)
+
+**功能**: 评估文本与原始问题的匹配程度
+
+**输入**: 原始问题 + 待评估文本
+**输出**:
+```python
+class RelevanceEvaluation:
+    relevance_score: float  # 0-1的相关性分数
+    reason: str            # 评估理由
+```
+
+**评估标准**:
+- 主题相关性
+- 要素覆盖度
+- 意图匹配度
+
+**示例**:
+- 原始问题: "川西秋季摄影"
+- 待评估: "川西旅游攻略"
+- 输出: score=0.75, reason="与川西相关但缺少秋季和摄影要素"
+
+### Agent 3: 加词选择专家 (word_selector)
+
+**功能**: 从候选词中选择最合适的词与 seed 组合
+
+**输入**: 原始问题 + 当前 seed + 候选词列表
+**输出**:
+```python
+class WordSelection:
+    selected_word: str       # 选择的词
+    combined_query: str      # 组合后的新query
+    reasoning: str           # 选择理由
+```
+
+**选择原则**:
+1. 选择与当前 seed 最相关的词
+2. 组合后的 query 语义通顺
+3. 符合搜索习惯
+4. 优先选择能扩展搜索范围的词
+
+**示例**:
+- seed: "川西"
+- 候选词: ["秋季", "摄影", "旅游"]
+- 输出: selected_word="秋季", combined_query="川西秋季"
+
+---
+
+## 🔄 核心流程
+
+### 阶段 0: 初始化 (initialize)
+
+**目标**: 从原始问题创建初始数据结构
+
+**流程**:
+
+```
+步骤1: 分词
+o → [word_segmenter] → WordSegmentation → seg_list
+
+步骤2: 评估分词
+for each seg in seg_list:
+    seg + o → [relevance_evaluator] → score + reason
+    更新 seg.score_with_o, seg.reason
+
+步骤3: 构建 word_list_1
+seg_list → word_list_1 (直接转换)
+
+步骤4: 构建 q_list_1
+seg_list → q_list_1 (from_source="seg")
+
+步骤5: 构建 seed_list
+seg_list → seed_list (from_type="seg")
+```
+
+**输入**:
+- `o`: 原始问题(例如: "如何获取川西秋季风光摄影素材?")
+
+**输出**:
+- `seg_list`: 分词结果列表
+- `word_list_1`: 初始词库
+- `q_list_1`: 第一轮待处理查询列表
+- `seed_list`: 初始种子列表
+
+**示例数据流**:
+```
+o = "川西秋季摄影素材"
+    ↓
+seg_list = [
+    Seg(text="川西", score_with_o=0.85),
+    Seg(text="秋季", score_with_o=0.90),
+    Seg(text="摄影", score_with_o=0.88),
+    Seg(text="素材", score_with_o=0.75)
+]
+    ↓
+word_list_1 = [Word("川西"), Word("秋季"), ...]
+q_list_1 = [Q("川西"), Q("秋季"), ...]
+seed_list = [Seed("川西"), Seed("秋季"), ...]
+```
+
+---
+
+### 阶段 N: 轮次迭代 (run_round)
+
+**目标**: 基于当前 q_list 扩展搜索,生成下一轮的数据
+
+**输入**:
+- `round_num`: 轮次编号
+- `q_list`: 当前轮的查询列表
+- `word_list`: 当前词库
+- `seed_list`: 当前种子列表
+- `sug_threshold`: 建议词阈值(默认 0.7)
+
+**输出**:
+- `word_list_next`: 下一轮词库
+- `q_list_next`: 下一轮查询列表
+- `seed_list_next`: 下一轮种子列表
+- `search_list`: 本轮搜索结果
+
+#### 步骤1: 请求建议词
+
+```python
+for each q in q_list:
+    sug_texts = xiaohongshu_api.get_recommendations(q.text)
+    for sug_text in sug_texts:
+        sug_list.append(Sug(
+            text=sug_text,
+            from_q=QFromQ(text=q.text, score=q.score_with_o)
+        ))
+```
+
+**并发处理**: 所有 q 的请求可以并发执行
+
+**数据流**:
+```
+q_list = [Q("川西"), Q("秋季")]
+    ↓ [小红书API]
+sug_list_list = [
+    [Sug("川西旅游"), Sug("川西攻略"), ...],  # 来自 "川西"
+    [Sug("秋季景色"), Sug("秋季摄影"), ...]   # 来自 "秋季"
+]
+```
+
+#### 步骤2: 评估建议词
+
+```python
+async def evaluate_sug(sug: Sug) -> Sug:
+    sug.score_with_o, sug.reason = await evaluate_with_o(sug.text, o)
+    return sug
+
+# 并发评估所有 sug
+await asyncio.gather(*[evaluate_sug(sug) for sug in all_sugs])
+```
+
+**评估标准**: 使用 relevance_evaluator Agent
+
+**数据流**:
+```
+Sug("川西旅游") + o → score=0.75, reason="..."
+Sug("秋季摄影") + o → score=0.92, reason="..."
+```
+
+#### 步骤3: 构建 search_list(搜索高分建议词)
+
+```python
+high_score_sugs = [sug for sug in all_sugs if sug.score_with_o > sug_threshold]
+
+async def search_for_sug(sug: Sug) -> Search:
+    result = xiaohongshu_search.search(sug.text)
+    posts = process_notes(result)
+    return Search(text=sug.text, post_list=posts, ...)
+
+search_list = await asyncio.gather(*[search_for_sug(sug) for sug in high_score_sugs])
+```
+
+**阈值过滤**: 只搜索评分 > `sug_threshold` 的建议词
+
+**并发搜索**: 所有高分 sug 并发搜索
+
+**数据流**:
+```
+high_score_sugs = [Sug("秋季摄影", score=0.92), ...]
+    ↓ [小红书搜索API]
+search_list = [
+    Search(text="秋季摄影", post_list=[Post(...), ...])
+]
+```
+
+#### 步骤4: 构建 word_list_next
+
+```python
+word_list_next = word_list.copy()  # 暂时直接复制
+```
+
+**说明**: 当前版本词库保持不变,未来可扩展从 sug 中提取新词
+
+#### 步骤5: 构建 q_list_next
+
+**5.1 为每个 seed 加词**
+
+```python
+for each seed in seed_list:
+    # 过滤候选词
+    candidate_words = [w for w in word_list_next
+                       if w.text not in seed.text
+                       and w.text not in seed.added_words]
+
+    # Agent 选词
+    selection_input = f"""
+    原始问题: {o}
+    当前Seed: {seed.text}
+    候选词: {candidate_words}
+    """
+    result = await Runner.run(word_selector, selection_input)
+
+    # 创建新 query
+    new_q = Q(
+        text=result.combined_query,
+        score_with_o=...,
+        from_source="add"
+    )
+    q_list_next.append(new_q)
+
+    # 更新 seed
+    seed.added_words.append(result.selected_word)
+```
+
+**关键逻辑**:
+- 避免重复: 词不在 seed.text 中且未被添加过
+- Agent 智能选择: 使用 word_selector 选择最佳组合
+- 评估新 query: 评估组合后的 query 与原始问题的相关度
+
+**示例**:
+```
+seed = Seed("川西", added_words=[])
+candidate_words = ["秋季", "摄影"]
+    ↓ [word_selector]
+selected_word = "秋季"
+combined_query = "川西秋季"
+    ↓ [relevance_evaluator]
+new_q = Q("川西秋季", score=0.88, from_source="add")
+```
+
+**5.2 高分 sug 加入 q_list_next**
+
+```python
+for sug in all_sugs:
+    if sug.score_with_o > sug.from_q.score_with_o:
+        new_q = Q(
+            text=sug.text,
+            score_with_o=sug.score_with_o,
+            from_source="sug"
+        )
+        q_list_next.append(new_q)
+```
+
+**条件**: sug 分数 > 来源 query 分数
+
+**示例**:
+```
+sug = Sug("秋季摄影技巧", score=0.92, from_q=Q("秋季", score=0.85))
+    ↓ (0.92 > 0.85)
+q_list_next.append(Q("秋季摄影技巧", score=0.92, from_source="sug"))
+```
+
+#### 步骤6: 更新 seed_list
+
+```python
+seed_list_next = seed_list.copy()  # 保留原有 seed
+
+for sug in all_sugs:
+    if (sug.score_with_o > sug.from_q.score_with_o
+        and sug.text not in existing_seed_texts):
+        new_seed = Seed(
+            text=sug.text,
+            from_type="sug",
+            score_with_o=sug.score_with_o
+        )
+        seed_list_next.append(new_seed)
+```
+
+**条件**:
+1. sug 分数 > 来源 query 分数
+2. sug 未在 seed_list 中出现过
+
+**示例**:
+```
+sug = Sug("川西秋季攻略", score=0.90, from_q=Q("川西", score=0.85))
+    ↓ (0.90 > 0.85 且未重复)
+seed_list_next.append(Seed("川西秋季攻略", from_type="sug"))
+```
+
+---
+
+### 主循环 (iterative_loop)
+
+**流程控制**:
+
+```python
+# 初始化
+seg_list, word_list, q_list, seed_list = await initialize(o, context)
+
+# 迭代
+round_num = 1
+while q_list and round_num <= max_rounds:
+    word_list, q_list, seed_list, search_list = await run_round(
+        round_num, q_list, word_list, seed_list, ...
+    )
+    all_search_list.extend(search_list)
+    round_num += 1
+
+return all_search_list
+```
+
+**终止条件**:
+1. `q_list` 为空(没有更多查询需要处理)
+2. 达到 `max_rounds` 限制
+
+**数据累积**: 所有轮次的 search_list 合并到 `all_search_list`
+
+---
+
+## 📊 数据流图
+
+### 完整数据流
+
+```
+输入:
+├─ input_dir/context.md  (原始需求 c)
+└─ input_dir/q.md        (原始问题 o)
+    ↓
+[初始化]
+o → seg_list → word_list_1, q_list_1, seed_list
+    ↓
+[第1轮]
+q_list_1 → sug_list_1 → search_list_1
+         → q_list_2, seed_list_2 (通过加词+高分sug)
+    ↓
+[第2轮]
+q_list_2 → sug_list_2 → search_list_2
+         → q_list_3, seed_list_3
+    ↓
+[第N轮] ...
+    ↓
+输出:
+├─ all_search_list (所有搜索结果)
+├─ log_dir/run_context.json (运行上下文)
+├─ log_dir/search_results.json (详细搜索结果)
+└─ log_dir/visualization.html (可视化HTML)
+```
+
+### 每轮数据变化
+
+```
+轮次输入                          轮次输出
+┌─────────────────┐             ┌─────────────────┐
+│ q_list          │──┐          │ q_list_next     │
+│ word_list       │  │          │ word_list_next  │
+│ seed_list       │  │          │ seed_list_next  │
+└─────────────────┘  │          │ search_list     │
+                     │          └─────────────────┘
+                     ↓
+            ┌──────────────────┐
+            │   run_round()    │
+            │                  │
+            │ 1. 请求sug       │
+            │ 2. 评估sug       │
+            │ 3. 搜索高分sug   │
+            │ 4. 为seed加词    │
+            │ 5. 构建q_next    │
+            │ 6. 更新seed_list │
+            └──────────────────┘
+```
+
+---
+
+## 🎯 关键算法
+
+### 1. 相关度评分机制
+
+**评分函数**: `evaluate_with_o(text, o)`
+
+**输入**:
+- `text`: 待评估文本
+- `o`: 原始问题
+
+**输出**: `(score, reason)`
+
+**实现**:
+```python
+async def evaluate_with_o(text: str, o: str) -> tuple[float, str]:
+    eval_input = f"""
+    <原始问题>{o}</原始问题>
+    <当前文本>{text}</当前文本>
+    请评估当前文本与原始问题的相关度。
+    """
+    result = await Runner.run(relevance_evaluator, eval_input)
+    return result.final_output.relevance_score, result.final_output.reason
+```
+
+**应用场景**:
+- 评估分词与原始问题的相关度
+- 评估 sug 与原始问题的相关度
+- 评估新组合 query 与原始问题的相关度
+
+### 2. 加词策略
+
+**目标**: 从词库中为 seed 选择最佳词进行组合
+
+**候选词过滤**:
+```python
+candidate_words = [
+    w for w in word_list
+    if w.text not in seed.text           # 词不在seed中
+    and w.text not in seed.added_words   # 词未被添加过
+]
+```
+
+**智能选择**:
+```python
+selection_input = f"""
+<原始问题>{o}</原始问题>
+<当前Seed>{seed.text}</当前Seed>
+<候选词列表>{', '.join([w.text for w in candidate_words])}</候选词列表>
+请从候选词中选择一个最合适的词,与当前seed组合成新的query。
+"""
+result = await Runner.run(word_selector, selection_input)
+```
+
+**验证和评估**:
+```python
+# 验证选择的词在候选列表中
+if selection.selected_word not in [w.text for w in candidate_words]:
+    continue
+
+# 评估组合后的query
+new_q_score, new_q_reason = await evaluate_with_o(
+    selection.combined_query, o
+)
+```
+
+### 3. Sug 晋升机制
+
+**晋升到 q_list 的条件**:
+```python
+if sug.score_with_o > sug.from_q.score_with_o:
+    q_list_next.append(Q(
+        text=sug.text,
+        score_with_o=sug.score_with_o,
+        from_source="sug"
+    ))
+```
+
+**晋升到 seed_list 的条件**:
+```python
+if (sug.score_with_o > sug.from_q.score_with_o
+    and sug.text not in existing_seed_texts):
+    seed_list_next.append(Seed(
+        text=sug.text,
+        from_type="sug",
+        score_with_o=sug.score_with_o
+    ))
+```
+
+**逻辑**: 只有当 sug 的评分超过其来源 query 时,才认为 sug 是更优的查询词
+
+### 4. 搜索阈值过滤
+
+**目标**: 只搜索高质量的建议词
+
+**实现**:
+```python
+high_score_sugs = [
+    sug for sug in all_sugs
+    if sug.score_with_o > sug_threshold
+]
+
+# 并发搜索
+search_list = await asyncio.gather(*[
+    search_for_sug(sug) for sug in high_score_sugs
+])
+```
+
+**默认阈值**: 0.7(可通过 `--sug-threshold` 参数调整)
+
+---
+
+## 🔧 外部服务集成
+
+### 1. 小红书搜索推荐 API
+
+**类**: `XiaohongshuSearchRecommendations`
+
+**方法**: `get_recommendations(keyword: str) -> list[str]`
+
+**功能**: 获取指定关键词的搜索建议词
+
+**使用场景**: 在每轮中为 q_list 中的每个 query 请求建议词
+
+### 2. 小红书搜索 API
+
+**类**: `XiaohongshuSearch`
+
+**方法**: `search(keyword: str) -> dict`
+
+**功能**: 搜索指定关键词,返回帖子列表
+
+**返回数据处理**:
+```python
+def process_note_data(note: dict) -> Post:
+    note_card = note.get("note_card", {})
+    return Post(
+        note_id=note.get("id"),
+        title=note_card.get("display_title"),
+        body_text=note_card.get("desc"),
+        type=note_card.get("type", "normal"),
+        images=[img.get("image_url") for img in note_card.get("image_list", [])],
+        interact_info={
+            "liked_count": ...,
+            "collected_count": ...,
+            "comment_count": ...,
+            "shared_count": ...
+        },
+        note_url=f"https://www.xiaohongshu.com/explore/{note.get('id')}"
+    )
+```
+
+---
+
+## 📝 日志和输出
+
+### 运行上下文 (run_context.json)
+
+**保存内容**:
+```json
+{
+  "version": "sug_v6_1_2_8.py",
+  "input_files": {...},
+  "c": "原始需求",
+  "o": "原始问题",
+  "log_dir": "...",
+  "log_url": "...",
+  "rounds": [
+    {
+      "round_num": 0,
+      "type": "initialization",
+      "seg_list": [...],
+      "word_list_1": [...],
+      "q_list_1": [...],
+      "seed_list": [...]
+    },
+    {
+      "round_num": 1,
+      "input_q_list": [...],
+      "sug_count": 20,
+      "high_score_sug_count": 5,
+      "search_count": 5,
+      "total_posts": 50,
+      "sug_details": {...},
+      "add_word_details": {...},
+      "search_results": [...]
+    },
+    ...
+  ],
+  "final_output": "..."
+}
+```
+
+### 搜索结果 (search_results.json)
+
+**保存内容**:
+```json
+[
+  {
+    "text": "秋季摄影",
+    "score_with_o": 0.92,
+    "reason": "...",
+    "from_q": {
+      "text": "秋季",
+      "score_with_o": 0.85
+    },
+    "post_list": [
+      {
+        "note_id": "...",
+        "note_url": "...",
+        "title": "...",
+        "body_text": "...",
+        "images": [...],
+        "interact_info": {...}
+      },
+      ...
+    ]
+  },
+  ...
+]
+```
+
+### 可视化 HTML
+
+**生成方式**:
+```python
+subprocess.run([
+    "node",
+    "visualization/sug_v6_1_2_8/index.js",
+    abs_context_file,
+    abs_output_html
+])
+```
+
+**依赖**: Node.js + React + esbuild
+
+**生成的文件**: `log_dir/visualization.html`
+
+---
+
+## 🚀 使用方法
+
+### 命令行参数
+
+```bash
+python3 sug_v6_1_2_8.py \
+  --input-dir "input/旅游/如何获取川西秋季风光摄影素材?" \
+  --max-rounds 4 \
+  --sug-threshold 0.7 \
+  --visualize
+```
+
+**参数说明**:
+
+| 参数 | 类型 | 默认值 | 说明 |
+|------|------|--------|------|
+| `--input-dir` | str | `input/旅游-逸趣玩旅行/...` | 输入目录路径 |
+| `--max-rounds` | int | 4 | 最大迭代轮数 |
+| `--sug-threshold` | float | 0.7 | 建议词评分阈值 |
+| `--visualize` | flag | True | 是否生成可视化 |
+
+### 输入文件结构
+
+```
+input_dir/
+├── context.md   # 原始需求描述
+└── q.md         # 原始问题
+```
+
+### 输出文件结构
+
+```
+input_dir/output/sug_v6_1_2_8/{timestamp}/
+├── run_context.json      # 运行上下文
+├── search_results.json   # 详细搜索结果
+└── visualization.html    # 可视化页面
+```
+
+---
+
+## 🎨 并发优化
+
+### 并发点
+
+1. **分词评估**: 所有 seg 并发评估
+   ```python
+   await asyncio.gather(*[evaluate_seg(seg) for seg in seg_list])
+   ```
+
+2. **Sug 评估**: 所有 sug 并发评估
+   ```python
+   await asyncio.gather(*[evaluate_sug(sug) for sug in all_sugs])
+   ```
+
+3. **搜索**: 所有高分 sug 并发搜索
+   ```python
+   await asyncio.gather(*[search_for_sug(sug) for sug in high_score_sugs])
+   ```
+
+### 串行点
+
+1. **分词**: 必须先完成分词才能评估
+2. **轮次迭代**: 必须按顺序执行各轮
+3. **加词选择**: 每个 seed 的加词必须等待 Agent 返回
+
+---
+
+## 🔍 核心特点
+
+### 1. 迭代扩展
+- 从原始问题出发,逐轮扩展搜索词
+- 通过 seed + word 组合生成新查询
+- 通过 sug 晋升机制引入新的高质量查询
+
+### 2. 智能评分
+- 所有文本与原始问题的相关度都通过 LLM 评估
+- 评分结果用于过滤、排序、晋升决策
+
+### 3. 多 Agent 协作
+- 分词专家: 拆分原始问题
+- 相关度评估专家: 统一评分标准
+- 加词选择专家: 智能组合词汇
+
+### 4. 数据驱动
+- 完整记录每轮的输入输出
+- 可追溯每个 query/sug 的来源
+- 支持可视化分析
+
+### 5. 高并发
+- 利用 asyncio 实现高并发
+- 评估、搜索等操作并行执行
+- 提升整体执行效率
+
+---
+
+## 🐛 潜在问题和改进方向
+
+### 1. 词库静态
+**问题**: word_list 在初始化后不再更新,可能错过新的有价值的词
+
+**改进方向**:
+- 从高分 sug 中提取新词加入 word_list
+- 从搜索结果的标题/正文中提取关键词
+
+### 2. 加词盲目性
+**问题**: 每个 seed 每轮必须加一个词,即使候选词质量不高
+
+**改进方向**:
+- 增加加词的评分阈值
+- 允许 seed 在某轮跳过加词
+
+### 3. Sug 重复
+**问题**: 不同 query 可能返回相同的 sug,导致重复搜索
+
+**改进方向**:
+- 全局去重 sug
+- 记录已搜索的 query,避免重复搜索
+
+### 4. 搜索结果未利用
+**问题**: 搜索到的帖子内容没有被进一步分析和利用
+
+**改进方向**:
+- 分析帖子标题/内容提取新的关键词
+- 评估帖子质量,作为 query 质量的反馈
+
+### 5. 阈值固定
+**问题**: sug_threshold 固定,可能导致某些轮次没有搜索结果
+
+**改进方向**:
+- 动态调整阈值
+- 保证每轮至少有一定数量的搜索
+
+---
+
+## 📈 性能分析
+
+### 时间复杂度
+
+假设:
+- 每轮 q_list 大小: `Q`
+- 每个 q 的 sug 数量: `S`
+- 每轮 seed 数量: `K`
+- 最大轮数: `R`
+
+**每轮时间复杂度**:
+- 请求 sug: `O(Q)`(并发)
+- 评估 sug: `O(Q * S)`(并发)
+- 搜索: `O(高分sug数量)`(并发)
+- 加词: `O(K * word_list大小)`(串行,但每个加词操作并发评估)
+
+**总时间复杂度**: `O(R * (Q + Q*S + K*W))`
+
+### 空间复杂度
+
+- `seg_list`: `O(分词数)`
+- `word_list`: `O(分词数)`(当前版本)
+- `q_list`: `O(Q)` 每轮
+- `seed_list`: `O(K)` 每轮
+- `sug_list`: `O(Q * S)` 每轮
+- `search_list`: `O(高分sug数) * O(每个搜索的帖子数)`
+
+**总空间复杂度**: `O(R * (Q*S + 高分sug数*帖子数))`
+
+---
+
+## 🎯 总结
+
+`sug_v6_1_2_8.py` 是一个设计精良的搜索查询优化系统,具有以下特点:
+
+### 优势
+1. ✅ **模块化设计**: 数据模型、Agent、流程控制分离清晰
+2. ✅ **智能化**: 利用多个 LLM Agent 实现分词、评估、选词
+3. ✅ **可扩展**: 通过迭代机制不断扩展搜索范围
+4. ✅ **高性能**: 大量使用并发优化执行效率
+5. ✅ **可追溯**: 完整记录每轮数据,支持可视化分析
+
+### 核心流程
+```
+原始问题 → 分词 → 评估 → 迭代(请求sug → 评估 → 搜索 → 加词 → 更新) → 输出结果
+```
+
+### 关键机制
+- **评分机制**: 统一的相关度评估标准
+- **晋升机制**: 高分 sug 晋升为 query 和 seed
+- **扩展机制**: seed + word 组合生成新 query
+- **过滤机制**: 阈值过滤低质量 sug
+
+### 适用场景
+- 搜索查询扩展和优化
+- 关键词发现和探索
+- 内容检索和推荐
+- 搜索效果分析
+
+---
+
+**文档生成时间**: 2025-11-03
+**代码版本**: sug_v6_1_2_8.py
+**作者**: Knowledge Agent Team

+ 0 - 651
sug_v6_1_intent_relevance.py

@@ -1,651 +0,0 @@
-import asyncio
-import json
-import os
-import argparse
-from datetime import datetime
-
-from agents import Agent, Runner
-from lib.my_trace import set_trace
-from typing import Literal
-from pydantic import BaseModel, Field
-
-from lib.utils import read_file_as_string
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-
-    # 探索阶段记录
-    keywords: list[str] | None = Field(default=None, description="提取的关键词")
-    exploration_levels: list[dict] = Field(default_factory=list, description="每一层的探索结果")
-    level_analyses: list[dict] = Field(default_factory=list, description="每一层的主Agent分析")
-
-    # 最终结果
-    final_candidates: list[str] | None = Field(default=None, description="最终选出的候选query")
-    evaluation_results: list[dict] | None = Field(default=None, description="候选query的评估结果")
-    optimization_result: dict | None = Field(default=None, description="最终优化结果对象")
-    final_output: str | None = Field(default=None, description="最终输出结果(格式化文本)")
-
-
-# ============================================================================
-# Agent 1: 关键词提取专家
-# ============================================================================
-keyword_extraction_instructions = """
-你是关键词提取专家。给定一个搜索问题(含上下文),提取出**最细粒度的关键概念**。
-
-## 提取原则
-
-1. **细粒度优先**:拆分成最小的有意义单元
-   - 不要保留完整的长句
-   - 拆分成独立的、有搜索意义的词或短语
-
-2. **保留核心维度**:
-   - 地域/对象
-   - 时间
-   - 行为/意图:获取、教程、推荐、如何等
-   - 主题/领域
-   - 质量/属性
-
-3. **去掉无意义的虚词**:的、吗、呢等
-
-4. **保留领域专有词**:不要过度拆分专业术语
-   - 如果是常见的组合词,保持完整
-
-## 输出要求
-
-输出关键词列表,按重要性排序(最核心的在前)。
-""".strip()
-
-class KeywordList(BaseModel):
-    """关键词列表"""
-    keywords: list[str] = Field(..., description="提取的关键词,按重要性排序")
-    reasoning: str = Field(..., description="提取理由")
-
-keyword_extractor = Agent[None](
-    name="关键词提取专家",
-    instructions=keyword_extraction_instructions,
-    output_type=KeywordList,
-)
-
-
-# ============================================================================
-# Agent 2: 层级探索分析专家
-# ============================================================================
-level_analysis_instructions = """
-你是搜索空间探索分析专家。基于当前层级的探索结果,决定下一步行动。
-
-## 你的任务
-
-分析当前已探索的词汇空间,判断:
-1. **发现了什么有价值的信号?**
-2. **是否已经可以评估候选了?**
-3. **如果还不够,下一层应该探索什么组合?**
-
-## 分析维度
-
-### 1. 信号识别(最重要)
-
-看推荐词里**出现了什么主题**:
-
-**关键问题:**
-- 哪些推荐词**最接近原始需求**?
-- 哪些推荐词**揭示了有价值的方向**(即使不完全匹配)?
-- 哪些推荐词可以作为**下一层探索的桥梁**?
-- 系统对哪些概念理解得好?哪些理解偏了?
-
-### 2. 组合策略
-
-基于发现的信号,设计下一层探索:
-
-**组合类型:**
-
-a) **关键词直接组合**
-   - 两个关键词组合成新query
-
-b) **利用推荐词作为桥梁**(重要!)
-   - 发现某个推荐词很有价值 → 直接探索这个推荐词
-   - 或在推荐词基础上加其他关键词
-
-c) **跨层级组合**
-   - 结合多层发现的有价值推荐词
-   - 组合成更复杂的query
-
-### 3. 停止条件
-
-**何时可以评估候选?**
-
-满足以下之一:
-- 推荐词中出现了**明确包含原始需求多个核心要素的query**
-- 已经探索到**足够复杂的组合**(3-4个关键词),且推荐词相关
-- 探索了**3-4层**,信息已经足够丰富
-
-**何时继续探索?**
-- 当前推荐词太泛,没有接近原始需求
-- 发现了有价值的信号,但需要进一步组合验证
-- 层数还少(< 3层)
-
-## 输出要求
-
-### 1. key_findings
-总结当前层发现的关键信息,包括:
-- 哪些推荐词最有价值?
-- 系统对哪些概念理解得好/不好?
-- 发现了什么意外的方向?
-
-### 2. promising_signals
-列出最有价值的推荐词(来自任何已探索的query),每个说明为什么有价值
-
-### 3. should_evaluate_now
-是否已经可以开始评估候选了?true/false
-
-### 4. candidates_to_evaluate
-如果should_evaluate_now=true,列出应该评估的候选query
-- 可以是推荐词
-- 可以是自己构造的组合
-
-### 5. next_combinations
-如果should_evaluate_now=false,列出下一层应该探索的query组合
-
-### 6. reasoning
-详细的推理过程
-
-## 重要原则
-
-1. **不要过早评估**:至少探索2层,除非第一层就发现了完美匹配
-2. **充分利用推荐词**:推荐词是系统给的提示,要善用
-3. **保持探索方向的多样性**:不要只盯着一个方向
-4. **识别死胡同**:如果某个方向的推荐词一直不相关,果断放弃
-""".strip()
-
-class PromisingSignal(BaseModel):
-    """有价值的推荐词信号"""
-    query: str = Field(..., description="推荐词")
-    from_level: int = Field(..., description="来自哪一层")
-    reason: str = Field(..., description="为什么有价值")
-
-class LevelAnalysis(BaseModel):
-    """层级分析结果"""
-    key_findings: str = Field(..., description="当前层的关键发现")
-    promising_signals: list[PromisingSignal] = Field(..., description="有价值的推荐词信号")
-    should_evaluate_now: bool = Field(..., description="是否应该开始评估候选")
-    candidates_to_evaluate: list[str] = Field(default_factory=list, description="如果should_evaluate_now=true,要评估的候选query列表")
-    next_combinations: list[str] = Field(default_factory=list, description="如果should_evaluate_now=false,下一层要探索的query组合")
-    reasoning: str = Field(..., description="详细的推理过程")
-
-level_analyzer = Agent[None](
-    name="层级探索分析专家",
-    instructions=level_analysis_instructions,
-    output_type=LevelAnalysis,
-)
-
-
-# ============================================================================
-# Agent 3: 评估专家(简化版:意图匹配 + 相关性评分)
-# ============================================================================
-eval_instructions = """
-你是搜索query评估专家。给定原始问题和推荐query,评估两个维度。
-
-## 评估目标
-
-用这个推荐query搜索,能否找到满足原始需求的内容?
-
-## 两层评分
-
-### 1. intent_match(意图匹配)= true/false
-
-推荐query的**使用意图**是否与原问题一致?
-
-**核心问题:用户搜索这个推荐词,想做什么?**
-
-**判断标准:**
-- 原问题意图:找方法?找教程?找资源/素材?找工具?看作品?
-- 推荐词意图:如果用户搜索这个词,他的目的是什么?
-
-**示例:**
-- 原问题意图="找素材"
-  - ✅ true: "素材下载"、"素材网站"、"免费素材"(都是获取素材)
-  - ❌ false: "素材制作教程"、"如何制作素材"(意图变成学习了)
-
-- 原问题意图="学教程"
-  - ✅ true: "教程视频"、"教学步骤"、"入门指南"
-  - ❌ false: "成品展示"、"作品欣赏"(意图变成看作品了)
-
-**评分:**
-- true = 意图一致,搜索推荐词能达到原问题的目的
-- false = 意图改变,搜索推荐词无法达到原问题的目的
-
-### 2. relevance_score(相关性)= 0-1 连续分数
-
-推荐query在**主题、要素、属性**上与原问题的相关程度?
-
-**评估维度:**
-- 主题相关:核心主题是否匹配?(如:摄影、旅游、美食)
-- 要素覆盖:关键要素保留了多少?(如:地域、时间、对象、工具)
-- 属性匹配:质量、风格、特色等属性是否保留?
-
-**评分参考:**
-- 0.9-1.0 = 几乎完美匹配,所有核心要素都保留
-- 0.7-0.8 = 高度相关,核心要素保留,少数次要要素缺失
-- 0.5-0.6 = 中度相关,主题匹配但多个要素缺失
-- 0.3-0.4 = 低度相关,只有部分主题相关
-- 0-0.2 = 基本不相关
-
-## 评估策略
-
-1. **先判断 intent_match**:意图不匹配直接 false,无论相关性多高
-2. **再评估 relevance_score**:在意图匹配的前提下,计算相关性
-
-## 输出要求
-
-- intent_match: true/false
-- relevance_score: 0-1 的浮点数
-- reason: 详细的评估理由,需要说明:
-  - 原问题的意图是什么
-  - 推荐词的意图是什么
-  - 为什么判断意图匹配/不匹配
-  - 相关性分数的依据(哪些要素保留/缺失)
-""".strip()
-
-class RelevanceEvaluation(BaseModel):
-    """评估反馈模型 - 意图匹配 + 相关性"""
-    intent_match: bool = Field(..., description="意图是否匹配")
-    relevance_score: float = Field(..., description="相关性分数 0-1,分数越高越相关")
-    reason: str = Field(..., description="评估理由,需说明意图判断和相关性依据")
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_instructions,
-    output_type=RelevanceEvaluation,
-)
-
-
-# ============================================================================
-# 核心函数
-# ============================================================================
-
-async def extract_keywords(q: str) -> KeywordList:
-    """提取关键词"""
-    print("\n正在提取关键词...")
-    result = await Runner.run(keyword_extractor, q)
-    keyword_list: KeywordList = result.final_output
-    print(f"提取的关键词:{keyword_list.keywords}")
-    print(f"提取理由:{keyword_list.reasoning}")
-    return keyword_list
-
-
-async def explore_level(queries: list[str], level_num: int, context: RunContext) -> dict:
-    """探索一个层级(并发获取所有query的推荐词)"""
-    print(f"\n{'='*60}")
-    print(f"Level {level_num} 探索:{len(queries)} 个query")
-    print(f"{'='*60}")
-
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-
-    # 并发获取所有推荐词
-    async def get_single_sug(query: str):
-        print(f"  探索: {query}")
-        suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-        print(f"    → {len(suggestions) if suggestions else 0} 个推荐词")
-        return {
-            "query": query,
-            "suggestions": suggestions or []
-        }
-
-    results = await asyncio.gather(*[get_single_sug(q) for q in queries])
-
-    level_data = {
-        "level": level_num,
-        "timestamp": datetime.now().isoformat(),
-        "queries": results
-    }
-
-    context.exploration_levels.append(level_data)
-    return level_data
-
-
-async def analyze_level(level_data: dict, all_levels: list[dict], original_question: str, context: RunContext) -> LevelAnalysis:
-    """分析当前层级,决定下一步"""
-    print(f"\n正在分析 Level {level_data['level']}...")
-
-    # 构造输入
-    analysis_input = f"""
-<原始问题>
-{original_question}
-</原始问题>
-
-<已探索的所有层级>
-{json.dumps(all_levels, ensure_ascii=False, indent=2)}
-</已探索的所有层级>
-
-<当前层级>
-Level {level_data['level']}
-{json.dumps(level_data['queries'], ensure_ascii=False, indent=2)}
-</当前层级>
-
-请分析当前探索状态,决定下一步行动。
-"""
-
-    result = await Runner.run(level_analyzer, analysis_input)
-    analysis: LevelAnalysis = result.final_output
-
-    print(f"\n分析结果:")
-    print(f"  关键发现:{analysis.key_findings}")
-    print(f"  有价值的信号:{len(analysis.promising_signals)} 个")
-    print(f"  是否评估:{analysis.should_evaluate_now}")
-
-    if analysis.should_evaluate_now:
-        print(f"  候选query:{analysis.candidates_to_evaluate}")
-    else:
-        print(f"  下一层探索:{analysis.next_combinations}")
-
-    # 保存分析结果
-    context.level_analyses.append({
-        "level": level_data['level'],
-        "timestamp": datetime.now().isoformat(),
-        "analysis": analysis.model_dump()
-    })
-
-    return analysis
-
-
-async def evaluate_candidates(candidates: list[str], original_question: str, context: RunContext) -> list[dict]:
-    """评估候选query"""
-    print(f"\n{'='*60}")
-    print(f"评估 {len(candidates)} 个候选query")
-    print(f"{'='*60}")
-
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-
-    async def evaluate_single_candidate(candidate: str):
-        print(f"\n评估候选:{candidate}")
-
-        # 1. 获取推荐词
-        suggestions = xiaohongshu_api.get_recommendations(keyword=candidate)
-        print(f"  获取到 {len(suggestions) if suggestions else 0} 个推荐词")
-
-        if not suggestions:
-            return {
-                "candidate": candidate,
-                "suggestions": [],
-                "evaluations": []
-            }
-
-        # 2. 评估每个推荐词
-        async def eval_single_sug(sug: str):
-            eval_input = f"""
-<原始问题>
-{original_question}
-</原始问题>
-
-<待评估的推荐query>
-{sug}
-</待评估的推荐query>
-
-请评估该推荐query:
-1. intent_match: 意图是否匹配(true/false)
-2. relevance_score: 相关性分数(0-1)
-3. reason: 详细的评估理由
-"""
-            result = await Runner.run(evaluator, eval_input)
-            evaluation: RelevanceEvaluation = result.final_output
-            return {
-                "query": sug,
-                "intent_match": evaluation.intent_match,
-                "relevance_score": evaluation.relevance_score,
-                "reason": evaluation.reason,
-            }
-
-        evaluations = await asyncio.gather(*[eval_single_sug(s) for s in suggestions])
-
-        return {
-            "candidate": candidate,
-            "suggestions": suggestions,
-            "evaluations": evaluations
-        }
-
-    results = await asyncio.gather(*[evaluate_single_candidate(c) for c in candidates])
-
-    context.evaluation_results = results
-    return results
-
-
-def find_qualified_queries(evaluation_results: list[dict], min_relevance_score: float = 0.7) -> list[dict]:
-    """
-    查找所有合格的query
-
-    筛选标准:
-    1. intent_match = True(必须满足)
-    2. relevance_score >= min_relevance_score
-
-    返回:按 relevance_score 降序排列
-    """
-    all_qualified = []
-
-    for result in evaluation_results:
-        for eval_item in result.get("evaluations", []):
-            if (eval_item['intent_match'] is True
-                and eval_item['relevance_score'] >= min_relevance_score):
-                all_qualified.append({
-                    "from_candidate": result["candidate"],
-                    **eval_item
-                })
-
-    # 按relevance_score降序排列
-    return sorted(all_qualified, key=lambda x: x['relevance_score'], reverse=True)
-
-
-# ============================================================================
-# 主流程
-# ============================================================================
-
-async def progressive_exploration(context: RunContext, max_levels: int = 4) -> dict:
-    """
-    渐进式广度探索流程
-
-    Args:
-        context: 运行上下文
-        max_levels: 最大探索层数,默认4
-
-    返回格式:
-    {
-        "success": True/False,
-        "results": [...],
-        "message": "..."
-    }
-    """
-
-    # 阶段1:提取关键词(从原始问题提取)
-    keyword_result = await extract_keywords(context.q)
-    context.keywords = keyword_result.keywords
-
-    # 阶段2:渐进式探索
-    current_level = 1
-
-    # Level 1:单个关键词
-    level_1_queries = context.keywords[:7]  # 限制最多7个关键词
-    level_1_data = await explore_level(level_1_queries, current_level, context)
-
-    # 分析Level 1
-    analysis_1 = await analyze_level(level_1_data, context.exploration_levels, context.q, context)
-
-    if analysis_1.should_evaluate_now:
-        # 直接评估
-        eval_results = await evaluate_candidates(analysis_1.candidates_to_evaluate, context.q, context)
-        qualified = find_qualified_queries(eval_results, min_relevance_score=0.7)
-
-        if qualified:
-            return {
-                "success": True,
-                "results": qualified,
-                "message": f"Level 1 即找到 {len(qualified)} 个合格query"
-            }
-
-    # Level 2 及以后:迭代探索
-    for level_num in range(2, max_levels + 1):
-        # 获取上一层的分析结果
-        prev_analysis: LevelAnalysis = context.level_analyses[-1]["analysis"]
-        prev_analysis = LevelAnalysis(**prev_analysis)  # 转回对象
-
-        if not prev_analysis.next_combinations:
-            print(f"\nLevel {level_num-1} 分析后无需继续探索")
-            break
-
-        # 探索当前层
-        level_data = await explore_level(prev_analysis.next_combinations, level_num, context)
-
-        # 分析当前层
-        analysis = await analyze_level(level_data, context.exploration_levels, context.q, context)
-
-        if analysis.should_evaluate_now:
-            # 评估候选
-            eval_results = await evaluate_candidates(analysis.candidates_to_evaluate, context.q, context)
-            qualified = find_qualified_queries(eval_results, min_relevance_score=0.7)
-
-            if qualified:
-                return {
-                    "success": True,
-                    "results": qualified,
-                    "message": f"Level {level_num} 找到 {len(qualified)} 个合格query"
-                }
-
-    # 所有层探索完,降低标准
-    print(f"\n{'='*60}")
-    print(f"探索完 {max_levels} 层,降低标准(relevance_score >= 0.5)")
-    print(f"{'='*60}")
-
-    if context.evaluation_results:
-        acceptable = find_qualified_queries(context.evaluation_results, min_relevance_score=0.5)
-        if acceptable:
-            return {
-                "success": True,
-                "results": acceptable,
-                "message": f"找到 {len(acceptable)} 个可接受query(soft_score >= 0.5)"
-            }
-
-    # 完全失败
-    return {
-        "success": False,
-        "results": [],
-        "message": "探索完所有层级,未找到合格的推荐词"
-    }
-
-
-# ============================================================================
-# 输出格式化
-# ============================================================================
-
-def format_output(optimization_result: dict, context: RunContext) -> str:
-    """格式化输出结果"""
-    results = optimization_result.get("results", [])
-
-    output = f"原始问题:{context.q}\n"
-    output += f"提取的关键词:{', '.join(context.keywords or [])}\n"
-    output += f"探索层数:{len(context.exploration_levels)}\n"
-    output += f"状态:{optimization_result['message']}\n\n"
-
-    if optimization_result["success"] and results:
-        output += "合格的推荐query(按relevance_score降序):\n"
-        for i, result in enumerate(results, 1):
-            output += f"\n{i}. {result['query']}\n"
-            output += f"   - 来自候选:{result['from_candidate']}\n"
-            output += f"   - 意图匹配:{result['intent_match']} (True=意图一致)\n"
-            output += f"   - 相关性分数:{result['relevance_score']:.2f} (0-1,越高越相关)\n"
-            output += f"   - 评估理由:{result['reason']}\n"
-    else:
-        output += "结果:未找到合格推荐query\n"
-        if context.level_analyses:
-            last_analysis = context.level_analyses[-1]["analysis"]
-            output += f"\n最后一层分析:\n{last_analysis.get('key_findings', 'N/A')}\n"
-
-    return output.strip()
-
-
-# ============================================================================
-# 主函数
-# ============================================================================
-
-async def main(input_dir: str, max_levels: int = 4):
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]
-
-    # 日志保存目录
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-
-    # 执行渐进式探索
-    optimization_result = await progressive_exploration(run_context, max_levels=max_levels)
-
-    # 格式化输出
-    final_output = format_output(optimization_result, run_context)
-    print(f"\n{'='*60}")
-    print("最终结果")
-    print(f"{'='*60}")
-    print(final_output)
-
-    # 保存结果
-    run_context.optimization_result = optimization_result
-    run_context.final_output = final_output
-
-    # 保存 RunContext 到 log_dir
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具 - v6.1 意图匹配+相关性评分版")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    parser.add_argument(
-        "--max-levels",
-        type=int,
-        default=4,
-        help="最大探索层数,默认: 4"
-    )
-    args = parser.parse_args()
-
-    asyncio.run(main(args.input_dir, max_levels=args.max_levels))

+ 0 - 557
sug_v6_2_combinatorial.py

@@ -1,557 +0,0 @@
-import asyncio
-import json
-import os
-import argparse
-from datetime import datetime
-from itertools import combinations
-
-from agents import Agent, Runner
-from lib.my_trace import set_trace
-from typing import Literal
-from pydantic import BaseModel, Field
-
-from lib.utils import read_file_as_string
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-
-    # 分词和组合
-    keywords: list[str] | None = Field(default=None, description="提取的关键词")
-    query_combinations: dict[str, list[str]] = Field(default_factory=dict, description="各层级的query组合")
-
-    # 探索结果
-    all_sug_queries: list[dict] = Field(default_factory=list, description="所有获取到的推荐词")
-
-    # 评估结果
-    evaluation_results: list[dict] = Field(default_factory=list, description="所有推荐词的评估结果")
-    optimization_result: dict | None = Field(default=None, description="最终优化结果对象")
-    final_output: str | None = Field(default=None, description="最终输出结果(格式化文本)")
-
-
-# ============================================================================
-# Agent 1: 分词专家
-# ============================================================================
-segmentation_instructions = """
-你是中文分词专家。给定一个句子,将其分词。
-
-## 分词原则
-
-1. 去掉标点符号
-2. 拆分成最小的有意义单元
-3. 去掉助词、语气词、助动词
-4. 保留疑问词
-5. 保留实词:名词、动词、形容词、副词
-
-## 输出要求
-
-输出分词列表。
-""".strip()
-
-class SegmentationResult(BaseModel):
-    """分词结果"""
-    words: list[str] = Field(..., description="分词列表")
-    reasoning: str = Field(..., description="分词说明")
-
-segmenter = Agent[None](
-    name="分词专家",
-    instructions=segmentation_instructions,
-    output_type=SegmentationResult,
-)
-
-
-# ============================================================================
-# Agent 2: 评估专家(意图匹配 + 相关性评分)
-# ============================================================================
-eval_instructions = """
-你是搜索query评估专家。给定原始问题和推荐query,评估两个维度。
-
-## 评估目标
-
-用这个推荐query搜索,能否找到满足原始需求的内容?
-
-## 两层评分
-
-### 1. intent_match(意图匹配)= true/false
-
-推荐query的**使用意图**是否与原问题一致?
-
-**核心问题:用户搜索这个推荐词,想做什么?**
-
-**判断标准:**
-- 原问题意图:找方法?找教程?找资源/素材?找工具?看作品?
-- 推荐词意图:如果用户搜索这个词,他的目的是什么?
-
-**评分:**
-- true = 意图一致,搜索推荐词能达到原问题的目的
-- false = 意图改变,搜索推荐词无法达到原问题的目的
-
-### 2. relevance_score(相关性)= 0-1 连续分数
-
-推荐query在**主题、要素、属性**上与原问题的相关程度?
-
-**评估维度:**
-- 主题相关:核心主题是否匹配?(如:摄影、旅游、美食)
-- 要素覆盖:关键要素保留了多少?(如:地域、时间、对象、工具)
-- 属性匹配:质量、风格、特色等属性是否保留?
-
-**评分参考:**
-- 0.9-1.0 = 几乎完美匹配,所有核心要素都保留
-- 0.7-0.8 = 高度相关,核心要素保留,少数次要要素缺失
-- 0.5-0.6 = 中度相关,主题匹配但多个要素缺失
-- 0.3-0.4 = 低度相关,只有部分主题相关
-- 0-0.2 = 基本不相关
-
-## 评估策略
-
-1. **先判断 intent_match**:意图不匹配直接 false,无论相关性多高
-2. **再评估 relevance_score**:在意图匹配的前提下,计算相关性
-
-## 输出要求
-
-- intent_match: true/false
-- relevance_score: 0-1 的浮点数
-- reason: 详细的评估理由,需要说明:
-  - 原问题的意图是什么
-  - 推荐词的意图是什么
-  - 为什么判断意图匹配/不匹配
-  - 相关性分数的依据(哪些要素保留/缺失)
-""".strip()
-
-class RelevanceEvaluation(BaseModel):
-    """评估反馈模型 - 意图匹配 + 相关性"""
-    intent_match: bool = Field(..., description="意图是否匹配")
-    relevance_score: float = Field(..., description="相关性分数 0-1,分数越高越相关")
-    reason: str = Field(..., description="评估理由,需说明意图判断和相关性依据")
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_instructions,
-    output_type=RelevanceEvaluation,
-)
-
-
-# ============================================================================
-# 核心函数
-# ============================================================================
-
-async def segment_text(q: str) -> SegmentationResult:
-    """分词"""
-    print("\n正在分词...")
-    result = await Runner.run(segmenter, q)
-    seg_result: SegmentationResult = result.final_output
-    print(f"分词结果:{seg_result.words}")
-    print(f"分词说明:{seg_result.reasoning}")
-    return seg_result
-
-
-def generate_query_combinations(keywords: list[str], max_combination_size: int) -> dict[str, list[str]]:
-    """
-    生成query组合
-
-    Args:
-        keywords: 关键词列表
-        max_combination_size: 最大组合词数(N)
-
-    Returns:
-        {
-            "1-word": [...],
-            "2-word": [...],
-            "3-word": [...],
-            ...
-            "N-word": [...]
-        }
-    """
-    result = {}
-
-    for size in range(1, max_combination_size + 1):
-        if size > len(keywords):
-            break
-
-        combs = list(combinations(keywords, size))
-        queries = [''.join(comb) for comb in combs]  # 直接拼接,无空格
-        result[f"{size}-word"] = queries
-
-        print(f"\n{size}词组合:{len(queries)} 个")
-        if len(queries) <= 10:
-            for q in queries:
-                print(f"  - {q}")
-        else:
-            print(f"  - {queries[0]}")
-            print(f"  - {queries[1]}")
-            print(f"  ...")
-            print(f"  - {queries[-1]}")
-
-    return result
-
-
-async def fetch_suggestions_for_queries(queries: list[str], context: RunContext) -> list[dict]:
-    """
-    并发获取所有query的推荐词
-
-    Returns:
-        [
-            {
-                "query": "川西",
-                "suggestions": ["川西旅游", "川西攻略", ...],
-                "timestamp": "..."
-            },
-            ...
-        ]
-    """
-    print(f"\n{'='*60}")
-    print(f"获取推荐词:{len(queries)} 个query")
-    print(f"{'='*60}")
-
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-
-    async def get_single_sug(query: str):
-        print(f"  查询: {query}")
-        suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-        print(f"    → {len(suggestions) if suggestions else 0} 个推荐词")
-        return {
-            "query": query,
-            "suggestions": suggestions or [],
-            "timestamp": datetime.now().isoformat()
-        }
-
-    results = await asyncio.gather(*[get_single_sug(q) for q in queries])
-    return results
-
-
-async def evaluate_all_suggestions(sug_results: list[dict], original_question: str, context: RunContext) -> list[dict]:
-    """
-    评估所有推荐词
-
-    Args:
-        sug_results: 所有query的推荐词结果
-        original_question: 原始问题
-
-    Returns:
-        [
-            {
-                "source_query": "川西秋季",
-                "sug_query": "川西秋季旅游",
-                "intent_match": True,
-                "relevance_score": 0.8,
-                "reason": "..."
-            },
-            ...
-        ]
-    """
-    print(f"\n{'='*60}")
-    print(f"评估推荐词")
-    print(f"{'='*60}")
-
-    # 收集所有推荐词
-    all_evaluations = []
-
-    async def evaluate_single_sug(source_query: str, sug_query: str):
-        eval_input = f"""
-<原始问题>
-{original_question}
-</原始问题>
-
-<待评估的推荐query>
-{sug_query}
-</待评估的推荐query>
-
-请评估该推荐query:
-1. intent_match: 意图是否匹配(true/false)
-2. relevance_score: 相关性分数(0-1)
-3. reason: 详细的评估理由
-"""
-        result = await Runner.run(evaluator, eval_input)
-        evaluation: RelevanceEvaluation = result.final_output
-        return {
-            "source_query": source_query,
-            "sug_query": sug_query,
-            "intent_match": evaluation.intent_match,
-            "relevance_score": evaluation.relevance_score,
-            "reason": evaluation.reason,
-        }
-
-    # 并发评估所有推荐词
-    tasks = []
-    for sug_result in sug_results:
-        source_query = sug_result["query"]
-        for sug in sug_result["suggestions"]:
-            tasks.append(evaluate_single_sug(source_query, sug))
-
-    if tasks:
-        print(f"  总共需要评估 {len(tasks)} 个推荐词...")
-        all_evaluations = await asyncio.gather(*tasks)
-
-    context.evaluation_results = all_evaluations
-    return all_evaluations
-
-
-def find_qualified_queries(evaluations: list[dict], min_relevance_score: float = 0.7) -> list[dict]:
-    """
-    查找所有合格的query
-
-    筛选标准:
-    1. intent_match = True(必须满足)
-    2. relevance_score >= min_relevance_score
-
-    返回:按 relevance_score 降序排列
-    """
-    qualified = [
-        e for e in evaluations
-        if e['intent_match'] is True and e['relevance_score'] >= min_relevance_score
-    ]
-
-    # 按relevance_score降序排列
-    return sorted(qualified, key=lambda x: x['relevance_score'], reverse=True)
-
-
-# ============================================================================
-# 主流程
-# ============================================================================
-
-async def combinatorial_search(context: RunContext, max_combination_size: int = 1) -> dict:
-    """
-    组合式搜索流程
-
-    Args:
-        context: 运行上下文
-        max_combination_size: 最大组合词数(N),默认1
-
-    返回格式:
-    {
-        "success": True/False,
-        "results": [...],
-        "message": "..."
-    }
-    """
-
-    # 步骤1:分词
-    seg_result = await segment_text(context.q)
-    context.keywords = seg_result.words
-
-    # 步骤2:生成query组合
-    print(f"\n{'='*60}")
-    print(f"生成query组合(最大组合数:{max_combination_size})")
-    print(f"{'='*60}")
-    query_combinations = generate_query_combinations(context.keywords, max_combination_size)
-    context.query_combinations = query_combinations
-
-    # 步骤3:获取所有query的推荐词
-    all_queries = []
-    for level, queries in query_combinations.items():
-        all_queries.extend(queries)
-
-    sug_results = await fetch_suggestions_for_queries(all_queries, context)
-    context.all_sug_queries = sug_results
-
-    # 统计
-    total_sugs = sum(len(r["suggestions"]) for r in sug_results)
-    print(f"\n总共获取到 {total_sugs} 个推荐词")
-
-    # 步骤4:评估所有推荐词
-    evaluations = await evaluate_all_suggestions(sug_results, context.q, context)
-
-    # 步骤5:筛选合格query
-    qualified = find_qualified_queries(evaluations, min_relevance_score=0.7)
-
-    if qualified:
-        return {
-            "success": True,
-            "results": qualified,
-            "message": f"找到 {len(qualified)} 个合格query(intent_match=True 且 relevance>=0.7)"
-        }
-
-    # 降低标准
-    acceptable = find_qualified_queries(evaluations, min_relevance_score=0.5)
-    if acceptable:
-        return {
-            "success": True,
-            "results": acceptable,
-            "message": f"找到 {len(acceptable)} 个可接受query(intent_match=True 且 relevance>=0.5)"
-        }
-
-    # 完全失败:返回所有intent_match=True的
-    intent_matched = [e for e in evaluations if e['intent_match'] is True]
-    if intent_matched:
-        intent_matched_sorted = sorted(intent_matched, key=lambda x: x['relevance_score'], reverse=True)
-        return {
-            "success": False,
-            "results": intent_matched_sorted[:10],  # 只返回前10个
-            "message": f"未找到高相关性query,但有 {len(intent_matched)} 个意图匹配的推荐词"
-        }
-
-    return {
-        "success": False,
-        "results": [],
-        "message": "未找到任何意图匹配的推荐词"
-    }
-
-
-# ============================================================================
-# 输出格式化
-# ============================================================================
-
-def format_output(optimization_result: dict, context: RunContext) -> str:
-    """格式化输出结果"""
-    results = optimization_result.get("results", [])
-
-    output = f"原始问题:{context.q}\n"
-    output += f"提取的关键词:{', '.join(context.keywords or [])}\n"
-    output += f"关键词数量:{len(context.keywords or [])}\n"
-    output += f"\nquery组合统计:\n"
-    for level, queries in context.query_combinations.items():
-        output += f"  - {level}: {len(queries)} 个\n"
-
-    # 统计信息
-    total_queries = sum(len(q) for q in context.query_combinations.values())
-    total_sugs = sum(len(r["suggestions"]) for r in context.all_sug_queries)
-    total_evals = len(context.evaluation_results)
-
-    output += f"\n探索统计:\n"
-    output += f"  - 总query数:{total_queries}\n"
-    output += f"  - 总推荐词数:{total_sugs}\n"
-    output += f"  - 总评估数:{total_evals}\n"
-
-    output += f"\n状态:{optimization_result['message']}\n\n"
-
-    if optimization_result["success"] and results:
-        output += "=" * 60 + "\n"
-        output += "合格的推荐query(按relevance_score降序):\n"
-        output += "=" * 60 + "\n"
-        for i, result in enumerate(results[:20], 1):  # 只显示前20个
-            output += f"\n{i}. [{result['relevance_score']:.2f}] {result['sug_query']}\n"
-            output += f"   来源:{result['source_query']}\n"
-            output += f"   意图:{'✓ 匹配' if result['intent_match'] else '✗ 不匹配'}\n"
-            output += f"   理由:{result['reason'][:150]}...\n" if len(result['reason']) > 150 else f"   理由:{result['reason']}\n"
-    else:
-        output += "=" * 60 + "\n"
-        output += "结果:未找到足够相关的推荐query\n"
-        output += "=" * 60 + "\n"
-        if results:
-            output += "\n最接近的推荐词(前10个):\n\n"
-            for i, result in enumerate(results[:10], 1):
-                output += f"{i}. [{result['relevance_score']:.2f}] {result['sug_query']}\n"
-                output += f"   来源:{result['source_query']}\n"
-                output += f"   意图:{'✓ 匹配' if result['intent_match'] else '✗ 不匹配'}\n\n"
-
-        # 按source_query分组显示
-        output += "\n" + "=" * 60 + "\n"
-        output += "按查询词分组的推荐词情况:\n"
-        output += "=" * 60 + "\n"
-
-        for sug_data in context.all_sug_queries:
-            source_q = sug_data["query"]
-            sugs = sug_data["suggestions"]
-
-            # 找到这个source_query对应的所有评估
-            related_evals = [e for e in context.evaluation_results if e["source_query"] == source_q]
-            intent_match_count = sum(1 for e in related_evals if e["intent_match"])
-            avg_relevance = sum(e["relevance_score"] for e in related_evals) / len(related_evals) if related_evals else 0
-
-            output += f"\n查询:{source_q}\n"
-            output += f"  推荐词数:{len(sugs)}\n"
-            output += f"  意图匹配数:{intent_match_count}/{len(related_evals)}\n"
-            output += f"  平均相关性:{avg_relevance:.2f}\n"
-
-            # 显示前3个推荐词
-            if sugs:
-                output += f"  示例推荐词:\n"
-                for sug in sugs[:3]:
-                    eval_item = next((e for e in related_evals if e["sug_query"] == sug), None)
-                    if eval_item:
-                        output += f"    - {sug} [意图:{'✓' if eval_item['intent_match'] else '✗'}, 相关:{eval_item['relevance_score']:.2f}]\n"
-                    else:
-                        output += f"    - {sug}\n"
-
-    return output.strip()
-
-
-# ============================================================================
-# 主函数
-# ============================================================================
-
-async def main(input_dir: str, max_combination_size: int = 1):
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]
-
-    # 日志保存目录
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-
-    # 执行组合式搜索
-    optimization_result = await combinatorial_search(run_context, max_combination_size=max_combination_size)
-
-    # 格式化输出
-    final_output = format_output(optimization_result, run_context)
-    print(f"\n{'='*60}")
-    print("最终结果")
-    print(f"{'='*60}")
-    print(final_output)
-
-    # 保存结果
-    run_context.optimization_result = optimization_result
-    run_context.final_output = final_output
-
-    # 保存 RunContext 到 log_dir
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description="搜索query优化工具 - v6.2 组合式搜索版")
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    parser.add_argument(
-        "--max-combo",
-        type=int,
-        default=1,
-        help="最大组合词数(N),默认: 1"
-    )
-    args = parser.parse_args()
-
-    asyncio.run(main(args.input_dir, max_combination_size=args.max_combo))

+ 0 - 820
sug_v6_3_with_annotation.py

@@ -1,820 +0,0 @@
-import asyncio
-import json
-import os
-import argparse
-from datetime import datetime
-from itertools import combinations, permutations
-
-from agents import Agent, Runner
-from lib.my_trace import set_trace
-from typing import Literal
-from pydantic import BaseModel, Field
-
-from lib.utils import read_file_as_string
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-
-# ============================================================================
-# 并发控制配置
-# ============================================================================
-# API请求并发度(小红书接口)
-API_CONCURRENCY_LIMIT = 5
-
-# 模型评估并发度(GPT评估)
-MODEL_CONCURRENCY_LIMIT = 10
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-
-    # 问题标注
-    question_annotation: str | None = Field(default=None, description="问题的标注结果(三层)")
-
-    # 分词和组合
-    keywords: list[str] | None = Field(default=None, description="提取的关键词")
-    query_combinations: dict[str, list[str]] = Field(default_factory=dict, description="各层级的query组合")
-
-    # 探索结果
-    all_sug_queries: list[dict] = Field(default_factory=list, description="所有获取到的推荐词")
-
-    # 评估结果
-    evaluation_results: list[dict] = Field(default_factory=list, description="所有推荐词的评估结果")
-    optimization_result: dict | None = Field(default=None, description="最终优化结果对象")
-    final_output: str | None = Field(default=None, description="最终输出结果(格式化文本)")
-
-
-# ============================================================================
-# Agent 1: 问题标注专家
-# ============================================================================
-question_annotation_instructions = """
-你是搜索需求分析专家。给定问题(含需求背景),在原文上标注三层:本质、硬性、软性。
-
-## 判断标准
-
-**[本质]** - 问题的核心意图
-- 如何获取、教程、推荐、作品、测评等
-
-**[硬]** - 客观事实性约束(可明确验证、非主观判断)
-- 能明确区分类别的:地域、时间、对象、工具、操作类型
-- 特征:改变后得到完全不同类别的结果
-
-**[软]** - 主观判断性修饰(因人而异、程度性的)
-- 需要主观评价的:质量、速度、美观、特色、程度
-- 特征:改变后仍是同类结果,只是满足程度不同
-
-## 输出格式
-
-词语[本质-描述]、词语[硬-描述]、词语[软-描述]
-
-## 注意
-- 只输出标注后的字符串
-- 结合需求背景判断意图
-""".strip()
-
-question_annotator = Agent[None](
-    name="问题标注专家",
-    instructions=question_annotation_instructions,
-)
-
-
-# ============================================================================
-# Agent 2: 分词专家
-# ============================================================================
-segmentation_instructions = """
-你是中文分词专家。给定一个句子,将其分词。
-
-## 分词原则
-
-1. 去掉标点符号
-2. 拆分成最小的有意义单元
-3. 去掉助词、语气词、助动词
-4. 保留疑问词
-5. 保留实词:名词、动词、形容词、副词
-
-## 输出要求
-
-输出分词列表。
-""".strip()
-
-class SegmentationResult(BaseModel):
-    """分词结果"""
-    words: list[str] = Field(..., description="分词列表")
-    reasoning: str = Field(..., description="分词说明")
-
-segmenter = Agent[None](
-    name="分词专家",
-    instructions=segmentation_instructions,
-    output_type=SegmentationResult,
-)
-
-
-# ============================================================================
-# Agent 3: 评估专家(意图匹配 + 相关性评分)
-# ============================================================================
-eval_instructions = """
-你是搜索query评估专家。给定原始问题、问题标注和推荐query,评估两个维度。
-
-## 输入信息
-
-你会收到:
-1. 原始问题:用户的原始表述
-2. 问题标注:对原始问题的三层标注(本质、硬性、软性)
-3. 推荐query:待评估的推荐词
-
-## 评估目标
-
-用这个推荐query搜索,能否找到满足原始需求的内容?
-
-## 两层评分
-
-### 1. intent_match(意图匹配)= true/false
-
-推荐query的**使用意图**是否与原问题的**本质**一致?
-
-**核心:只关注[本质]标注**
-- 问题标注中的 `[本质-XXX]` 标记明确了用户的核心意图
-- 判断推荐词是否能达成这个核心意图
-
-**常见本质类型:**
-- 找方法/如何获取 → 推荐词应包含方法、途径、网站、渠道等
-- 找教程 → 推荐词应是教程、教学相关
-- 找资源/素材 → 推荐词应是资源、素材本身
-- 找工具 → 推荐词应是工具推荐
-- 看作品 → 推荐词应是作品展示
-
-**评分:**
-- true = 推荐词的意图与 `[本质]` 一致
-- false = 推荐词的意图与 `[本质]` 不一致
-
-### 2. relevance_score(相关性)= 0-1 连续分数
-
-在意图匹配的前提下,推荐query在**主题、要素、属性**上与原问题的相关程度?
-
-**评估维度:**
-- 主题相关:核心主题是否匹配?(如:摄影、旅游、美食)
-- 要素覆盖:`[硬-XXX]` 标记的硬性约束保留了多少?(地域、时间、对象、工具等)
-- 属性匹配:`[软-XXX]` 标记的软性修饰保留了多少?(质量、速度、美观等)
-
-**评分参考:**
-- 0.9-1.0 = 几乎完美匹配,[硬]和[软]标注的要素都保留
-- 0.7-0.8 = 高度相关,[硬]标注的要素都保留,[软]标注少数缺失
-- 0.5-0.6 = 中度相关,[硬]标注的要素保留大部分,[软]标注多数缺失
-- 0.3-0.4 = 低度相关,[硬]标注的要素部分缺失
-- 0-0.2 = 基本不相关,[硬]标注的要素大量缺失
-
-## 评估策略
-
-1. **先看[本质]判断 intent_match**:意图不匹配直接 false
-2. **再看[硬][软]评估 relevance_score**:计算要素和属性的保留程度
-
-## 输出要求
-
-- intent_match: true/false
-- relevance_score: 0-1 的浮点数
-- reason: 详细的评估理由,需要说明:
-  - 原问题的[本质]是什么,推荐词是否匹配这个本质
-  - [硬]约束哪些保留/缺失
-  - [软]修饰哪些保留/缺失
-  - 最终相关性分数的依据
-""".strip()
-
-class RelevanceEvaluation(BaseModel):
-    """评估反馈模型 - 意图匹配 + 相关性"""
-    intent_match: bool = Field(..., description="意图是否匹配")
-    relevance_score: float = Field(..., description="相关性分数 0-1,分数越高越相关")
-    reason: str = Field(..., description="评估理由,需说明意图判断和相关性依据")
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_instructions,
-    output_type=RelevanceEvaluation,
-)
-
-
-# ============================================================================
-# 核心函数
-# ============================================================================
-
-async def annotate_question(q_with_context: str) -> str:
-    """标注问题(三层)"""
-    print("\n正在标注问题...")
-    result = await Runner.run(question_annotator, q_with_context)
-    annotation = str(result.final_output)
-    print(f"问题标注完成:{annotation}")
-    return annotation
-
-
-async def segment_text(q: str) -> SegmentationResult:
-    """分词"""
-    print("\n正在分词...")
-    result = await Runner.run(segmenter, q)
-    seg_result: SegmentationResult = result.final_output
-    print(f"分词结果:{seg_result.words}")
-    print(f"分词说明:{seg_result.reasoning}")
-    return seg_result
-
-
-def generate_query_combinations(keywords: list[str], max_combination_size: int) -> dict[str, list[str]]:
-    """
-    生成query组合(考虑词的顺序)
-
-    Args:
-        keywords: 关键词列表
-        max_combination_size: 最大组合词数(N)
-
-    Returns:
-        {
-            "1-word": [...],
-            "2-word": [...],
-            "3-word": [...],
-            ...
-            "N-word": [...]
-        }
-    """
-    result = {}
-
-    for size in range(1, max_combination_size + 1):
-        if size > len(keywords):
-            break
-
-        # 1-word组合:不需要考虑顺序
-        if size == 1:
-            queries = keywords.copy()
-        else:
-            # 多词组合:先选择size个词(combinations),再排列(permutations)
-            all_queries = []
-            combs = list(combinations(keywords, size))
-            for comb in combs:
-                # 对每个组合生成所有排列
-                perms = list(permutations(comb))
-                for perm in perms:
-                    query = ''.join(perm)  # 直接拼接,无空格
-                    all_queries.append(query)
-
-            # 去重(虽然理论上不会重复,但保险起见)
-            queries = list(dict.fromkeys(all_queries))
-
-        result[f"{size}-word"] = queries
-
-        print(f"\n{size}词组合:共 {len(queries)} 个")
-        # 打印所有query,带序号
-        for i, q in enumerate(queries, 1):
-            print(f"  {i}. {q}")
-
-    return result
-
-
-async def fetch_and_evaluate_streaming(
-    queries: list[str],
-    original_question: str,
-    question_annotation: str,
-    context: RunContext
-) -> tuple[list[dict], list[dict]]:
-    """
-    流式处理:边获取推荐词边评估
-
-    Returns:
-        (sug_results, evaluations)
-    """
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-
-    # 创建信号量
-    api_semaphore = asyncio.Semaphore(API_CONCURRENCY_LIMIT)
-    model_semaphore = asyncio.Semaphore(MODEL_CONCURRENCY_LIMIT)
-
-    # 结果收集
-    sug_results = []
-    all_evaluations = []
-
-    # 统计
-    total_queries = len(queries)
-    completed_queries = 0
-    total_sugs = 0
-    completed_evals = 0
-
-    async def get_and_evaluate_single_query(query: str):
-        nonlocal completed_queries, total_sugs, completed_evals
-
-        # 步骤1:获取推荐词
-        async with api_semaphore:
-            suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-            sug_count = len(suggestions) if suggestions else 0
-
-            completed_queries += 1
-            total_sugs += sug_count
-
-            print(f"  [{completed_queries}/{total_queries}] {query} → {sug_count} 个推荐词")
-
-            sug_result = {
-                "query": query,
-                "suggestions": suggestions or [],
-                "timestamp": datetime.now().isoformat()
-            }
-            sug_results.append(sug_result)
-
-        # 步骤2:立即评估这些推荐词
-        if suggestions:
-            eval_tasks = []
-            for sug in suggestions:
-                eval_tasks.append(evaluate_single_sug_with_semaphore(
-                    query, sug, original_question, question_annotation, model_semaphore
-                ))
-
-            if eval_tasks:
-                evals = await asyncio.gather(*eval_tasks)
-                all_evaluations.extend(evals)
-                completed_evals += len(evals)
-                print(f"      ↳ 已评估 {len(evals)} 个,累计评估 {completed_evals} 个")
-
-    # 并发处理所有query
-    await asyncio.gather(*[get_and_evaluate_single_query(q) for q in queries])
-
-    # 保存到context
-    context.all_sug_queries = sug_results
-    context.evaluation_results = all_evaluations
-
-    print(f"\n总计:获取 {total_sugs} 个推荐词,完成 {completed_evals} 个评估")
-
-    return sug_results, all_evaluations
-
-
-async def evaluate_single_sug_with_semaphore(
-    source_query: str,
-    sug_query: str,
-    original_question: str,
-    question_annotation: str,
-    semaphore: asyncio.Semaphore
-) -> dict:
-    """带信号量的单个推荐词评估"""
-    async with semaphore:
-        eval_input = f"""
-<原始问题>
-{original_question}
-</原始问题>
-
-<问题标注(三层)>
-{question_annotation}
-</问题标注(三层)>
-
-<待评估的推荐query>
-{sug_query}
-</待评估的推荐query>
-
-请评估该推荐query:
-1. intent_match: 意图是否匹配(true/false)
-2. relevance_score: 相关性分数(0-1)
-3. reason: 详细的评估理由
-
-评估时请参考问题标注中的[本质]、[硬]、[软]标记。
-"""
-        result = await Runner.run(evaluator, eval_input)
-        evaluation: RelevanceEvaluation = result.final_output
-        return {
-            "source_query": source_query,
-            "sug_query": sug_query,
-            "intent_match": evaluation.intent_match,
-            "relevance_score": evaluation.relevance_score,
-            "reason": evaluation.reason,
-        }
-
-
-async def fetch_suggestions_for_queries(queries: list[str], context: RunContext) -> list[dict]:
-    """
-    并发获取所有query的推荐词(带并发控制)
-
-    Returns:
-        [
-            {
-                "query": "川西",
-                "suggestions": ["川西旅游", "川西攻略", ...],
-                "timestamp": "..."
-            },
-            ...
-        ]
-    """
-    print(f"\n{'='*60}")
-    print(f"获取推荐词:{len(queries)} 个query(并发度:{API_CONCURRENCY_LIMIT})")
-    print(f"{'='*60}")
-
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-
-    # 创建信号量控制并发
-    semaphore = asyncio.Semaphore(API_CONCURRENCY_LIMIT)
-
-    async def get_single_sug(query: str):
-        async with semaphore:
-            print(f"  查询: {query}")
-            suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-            print(f"    → {len(suggestions) if suggestions else 0} 个推荐词")
-            return {
-                "query": query,
-                "suggestions": suggestions or [],
-                "timestamp": datetime.now().isoformat()
-            }
-
-    results = await asyncio.gather(*[get_single_sug(q) for q in queries])
-    return results
-
-
-async def evaluate_all_suggestions(
-    sug_results: list[dict],
-    original_question: str,
-    question_annotation: str,
-    context: RunContext
-) -> list[dict]:
-    """
-    评估所有推荐词(带并发控制)
-
-    Args:
-        sug_results: 所有query的推荐词结果
-        original_question: 原始问题
-        question_annotation: 问题标注(三层)
-
-    Returns:
-        [
-            {
-                "source_query": "川西秋季",
-                "sug_query": "川西秋季旅游",
-                "intent_match": True,
-                "relevance_score": 0.8,
-                "reason": "..."
-            },
-            ...
-        ]
-    """
-    print(f"\n{'='*60}")
-    print(f"评估推荐词(并发度:{MODEL_CONCURRENCY_LIMIT})")
-    print(f"{'='*60}")
-
-    # 创建信号量控制并发
-    semaphore = asyncio.Semaphore(MODEL_CONCURRENCY_LIMIT)
-
-    # 收集所有推荐词
-    all_evaluations = []
-
-    async def evaluate_single_sug(source_query: str, sug_query: str):
-        async with semaphore:
-            eval_input = f"""
-<原始问题>
-{original_question}
-</原始问题>
-
-<问题标注(三层)>
-{question_annotation}
-</问题标注(三层)>
-
-<待评估的推荐query>
-{sug_query}
-</待评估的推荐query>
-
-请评估该推荐query:
-1. intent_match: 意图是否匹配(true/false)
-2. relevance_score: 相关性分数(0-1)
-3. reason: 详细的评估理由
-
-评估时请参考问题标注中的[本质]、[硬]、[软]标记。
-"""
-            result = await Runner.run(evaluator, eval_input)
-            evaluation: RelevanceEvaluation = result.final_output
-            return {
-                "source_query": source_query,
-                "sug_query": sug_query,
-                "intent_match": evaluation.intent_match,
-                "relevance_score": evaluation.relevance_score,
-                "reason": evaluation.reason,
-            }
-
-    # 并发评估所有推荐词
-    tasks = []
-    for sug_result in sug_results:
-        source_query = sug_result["query"]
-        for sug in sug_result["suggestions"]:
-            tasks.append(evaluate_single_sug(source_query, sug))
-
-    if tasks:
-        print(f"  总共需要评估 {len(tasks)} 个推荐词...")
-        all_evaluations = await asyncio.gather(*tasks)
-
-    context.evaluation_results = all_evaluations
-    return all_evaluations
-
-
-def find_qualified_queries(evaluations: list[dict], min_relevance_score: float = 0.7) -> list[dict]:
-    """
-    查找所有合格的query
-
-    筛选标准:
-    1. intent_match = True(必须满足)
-    2. relevance_score >= min_relevance_score
-
-    返回:按 relevance_score 降序排列
-    """
-    qualified = [
-        e for e in evaluations
-        if e['intent_match'] is True and e['relevance_score'] >= min_relevance_score
-    ]
-
-    # 按relevance_score降序排列
-    return sorted(qualified, key=lambda x: x['relevance_score'], reverse=True)
-
-
-# ============================================================================
-# 主流程
-# ============================================================================
-
-async def combinatorial_search(context: RunContext, max_combination_size: int = 1) -> dict:
-    """
-    组合式搜索流程(带问题标注)
-
-    Args:
-        context: 运行上下文
-        max_combination_size: 最大组合词数(N),默认1
-
-    返回格式:
-    {
-        "success": True/False,
-        "results": [...],
-        "message": "..."
-    }
-    """
-
-    # 步骤1:标注问题(三层)
-    annotation = await annotate_question(context.q_with_context)
-    context.question_annotation = annotation
-
-    # 步骤2:分词
-    seg_result = await segment_text(context.q)
-    context.keywords = seg_result.words
-
-    # 步骤3:生成query组合
-    print(f"\n{'='*60}")
-    print(f"生成query组合(最大组合数:{max_combination_size})")
-    print(f"{'='*60}")
-    query_combinations = generate_query_combinations(context.keywords, max_combination_size)
-    context.query_combinations = query_combinations
-
-    # 步骤4:获取所有query的推荐词,并流式评估
-    all_queries = []
-    for level, queries in query_combinations.items():
-        all_queries.extend(queries)
-
-    print(f"\n{'='*60}")
-    print(f"流式处理:边获取推荐词边评估(API并发度:{API_CONCURRENCY_LIMIT},模型并发度:{MODEL_CONCURRENCY_LIMIT})")
-    print(f"{'='*60}")
-
-    # 流式处理:边获取边评估
-    sug_results, evaluations = await fetch_and_evaluate_streaming(
-        all_queries, context.q, annotation, context
-    )
-
-    # 步骤6:筛选合格query
-    qualified = find_qualified_queries(evaluations, min_relevance_score=0.7)
-
-    if qualified:
-        return {
-            "success": True,
-            "results": qualified,
-            "message": f"找到 {len(qualified)} 个合格query(intent_match=True 且 relevance>=0.7)"
-        }
-
-    # 降低标准
-    acceptable = find_qualified_queries(evaluations, min_relevance_score=0.5)
-    if acceptable:
-        return {
-            "success": True,
-            "results": acceptable,
-            "message": f"找到 {len(acceptable)} 个可接受query(intent_match=True 且 relevance>=0.5)"
-        }
-
-    # 完全失败:返回所有intent_match=True的
-    intent_matched = [e for e in evaluations if e['intent_match'] is True]
-    if intent_matched:
-        intent_matched_sorted = sorted(intent_matched, key=lambda x: x['relevance_score'], reverse=True)
-        return {
-            "success": False,
-            "results": intent_matched_sorted[:10],  # 只返回前10个
-            "message": f"未找到高相关性query,但有 {len(intent_matched)} 个意图匹配的推荐词"
-        }
-
-    return {
-        "success": False,
-        "results": [],
-        "message": "未找到任何意图匹配的推荐词"
-    }
-
-
-# ============================================================================
-# 输出格式化
-# ============================================================================
-
-def format_output(optimization_result: dict, context: RunContext) -> str:
-    """格式化输出结果"""
-    results = optimization_result.get("results", [])
-
-    output = f"原始问题:{context.q}\n"
-    output += f"问题标注:{context.question_annotation}\n"
-    output += f"提取的关键词:{', '.join(context.keywords or [])}\n"
-    output += f"关键词数量:{len(context.keywords or [])}\n"
-    output += f"\nquery组合统计:\n"
-    for level, queries in context.query_combinations.items():
-        output += f"  - {level}: {len(queries)} 个\n"
-
-    # 统计信息
-    total_queries = sum(len(q) for q in context.query_combinations.values())
-    total_sugs = sum(len(r["suggestions"]) for r in context.all_sug_queries)
-    total_evals = len(context.evaluation_results)
-
-    output += f"\n探索统计:\n"
-    output += f"  - 总query数:{total_queries}\n"
-    output += f"  - 总推荐词数:{total_sugs}\n"
-    output += f"  - 总评估数:{total_evals}\n"
-
-    output += f"\n状态:{optimization_result['message']}\n\n"
-
-    if optimization_result["success"] and results:
-        output += "=" * 60 + "\n"
-        output += "合格的推荐query(按relevance_score降序):\n"
-        output += "=" * 60 + "\n"
-        for i, result in enumerate(results[:20], 1):  # 只显示前20个
-            output += f"\n{i}. [{result['relevance_score']:.2f}] {result['sug_query']}\n"
-            output += f"   来源:{result['source_query']}\n"
-            output += f"   意图:{'✓ 匹配' if result['intent_match'] else '✗ 不匹配'}\n"
-            output += f"   理由:{result['reason'][:150]}...\n" if len(result['reason']) > 150 else f"   理由:{result['reason']}\n"
-    else:
-        output += "=" * 60 + "\n"
-        output += "结果:未找到足够相关的推荐query\n"
-        output += "=" * 60 + "\n"
-        if results:
-            output += "\n最接近的推荐词(前10个):\n\n"
-            for i, result in enumerate(results[:10], 1):
-                output += f"{i}. [{result['relevance_score']:.2f}] {result['sug_query']}\n"
-                output += f"   来源:{result['source_query']}\n"
-                output += f"   意图:{'✓ 匹配' if result['intent_match'] else '✗ 不匹配'}\n\n"
-
-        # 按source_query分组显示
-        output += "\n" + "=" * 60 + "\n"
-        output += "按查询词分组的推荐词情况:\n"
-        output += "=" * 60 + "\n"
-
-        for sug_data in context.all_sug_queries:
-            source_q = sug_data["query"]
-            sugs = sug_data["suggestions"]
-
-            # 找到这个source_query对应的所有评估
-            related_evals = [e for e in context.evaluation_results if e["source_query"] == source_q]
-            intent_match_count = sum(1 for e in related_evals if e["intent_match"])
-            avg_relevance = sum(e["relevance_score"] for e in related_evals) / len(related_evals) if related_evals else 0
-
-            output += f"\n查询:{source_q}\n"
-            output += f"  推荐词数:{len(sugs)}\n"
-            output += f"  意图匹配数:{intent_match_count}/{len(related_evals)}\n"
-            output += f"  平均相关性:{avg_relevance:.2f}\n"
-
-            # 显示前3个推荐词
-            if sugs:
-                output += f"  示例推荐词:\n"
-                for sug in sugs[:3]:
-                    eval_item = next((e for e in related_evals if e["sug_query"] == sug), None)
-                    if eval_item:
-                        output += f"    - {sug} [意图:{'✓' if eval_item['intent_match'] else '✗'}, 相关:{eval_item['relevance_score']:.2f}]\n"
-                    else:
-                        output += f"    - {sug}\n"
-
-    return output.strip()
-
-
-# ============================================================================
-# 主函数
-# ============================================================================
-
-async def main(
-    input_dir: str,
-    max_combination_size: int = 1,
-    api_concurrency: int = API_CONCURRENCY_LIMIT,
-    model_concurrency: int = MODEL_CONCURRENCY_LIMIT
-):
-    # 更新全局并发配置
-    global API_CONCURRENCY_LIMIT, MODEL_CONCURRENCY_LIMIT
-    API_CONCURRENCY_LIMIT = api_concurrency
-    MODEL_CONCURRENCY_LIMIT = model_concurrency
-
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]
-
-    # 日志保存目录
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-
-    print(f"\n{'='*60}")
-    print(f"并发配置")
-    print(f"{'='*60}")
-    print(f"API请求并发度:{API_CONCURRENCY_LIMIT}")
-    print(f"模型评估并发度:{MODEL_CONCURRENCY_LIMIT}")
-
-    # 执行组合式搜索(带问题标注)
-    optimization_result = await combinatorial_search(run_context, max_combination_size=max_combination_size)
-
-    # 格式化输出
-    final_output = format_output(optimization_result, run_context)
-    print(f"\n{'='*60}")
-    print("最终结果")
-    print(f"{'='*60}")
-    print(final_output)
-
-    # 保存结果
-    run_context.optimization_result = optimization_result
-    run_context.final_output = final_output
-
-    # 保存 RunContext 到 log_dir
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(
-        description="搜索query优化工具 - v6.3 组合式搜索+问题标注版",
-        formatter_class=argparse.RawDescriptionHelpFormatter,
-        epilog="""
-示例:
-  # 默认参数
-  python sug_v6_3_with_annotation.py
-
-  # 2词组合,API并发5,模型并发20
-  python sug_v6_3_with_annotation.py --max-combo 2 --api-concurrency 5 --model-concurrency 20
-
-  # 3词组合,降低并发度
-  python sug_v6_3_with_annotation.py --max-combo 3 --api-concurrency 3 --model-concurrency 10
-        """
-    )
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    parser.add_argument(
-        "--max-combo",
-        type=int,
-        default=1,
-        help="最大组合词数(N),默认: 1"
-    )
-    parser.add_argument(
-        "--api-concurrency",
-        type=int,
-        default=API_CONCURRENCY_LIMIT,
-        help=f"API请求并发度,默认: {API_CONCURRENCY_LIMIT}"
-    )
-    parser.add_argument(
-        "--model-concurrency",
-        type=int,
-        default=MODEL_CONCURRENCY_LIMIT,
-        help=f"模型评估并发度,默认: {MODEL_CONCURRENCY_LIMIT}"
-    )
-    args = parser.parse_args()
-
-    asyncio.run(main(
-        args.input_dir,
-        max_combination_size=args.max_combo,
-        api_concurrency=args.api_concurrency,
-        model_concurrency=args.model_concurrency
-    ))

+ 0 - 966
sug_v6_4_with_annotation.py

@@ -1,966 +0,0 @@
-import asyncio
-import json
-import os
-import argparse
-from datetime import datetime
-from itertools import combinations, permutations
-
-from agents import Agent, Runner
-from lib.my_trace import set_trace
-from typing import Literal
-from lib.client import get_model
-from pydantic import BaseModel, Field
-
-from lib.utils import read_file_as_string
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-
-# ============================================================================
-# 并发控制配置
-# ============================================================================
-# API请求并发度(小红书接口)
-API_CONCURRENCY_LIMIT = 5
-
-# 模型评估并发度(GPT评估)
-MODEL_CONCURRENCY_LIMIT = 10
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-
-    # 问题标注
-    question_annotation: str | None = Field(default=None, description="问题的标注结果(三层)")
-
-    # 分词和组合
-    keywords: list[str] | None = Field(default=None, description="提取的关键词")
-    query_combinations: dict[str, list[str]] = Field(default_factory=dict, description="各层级的query组合")
-
-    # v6.4 新增:剪枝记录
-    pruning_info: dict[str, dict] = Field(default_factory=dict, description="各层级的剪枝信息")
-
-    # 探索结果
-    all_sug_queries: list[dict] = Field(default_factory=list, description="所有获取到的推荐词")
-
-    # 评估结果
-    evaluation_results: list[dict] = Field(default_factory=list, description="所有推荐词的评估结果")
-    optimization_result: dict | None = Field(default=None, description="最终优化结果对象")
-    final_output: str | None = Field(default=None, description="最终输出结果(格式化文本)")
-
-
-# ============================================================================
-# Agent 1: 问题标注专家
-# ============================================================================
-question_annotation_instructions = """
-你是搜索需求分析专家。给定问题(含需求背景),在原文上标注三层:本质、硬性、软性。
-
-## 判断标准
-
-**[本质]** - 问题的核心意图
-- 如何获取、教程、推荐、作品、测评等
-
-**[硬]** - 客观事实性约束(可明确验证、非主观判断)
-- 能明确区分类别的:地域、时间、对象、工具、操作类型
-- 特征:改变后得到完全不同类别的结果
-
-**[软]** - 主观判断性修饰(因人而异、程度性的)
-- 需要主观评价的:质量、速度、美观、特色、程度
-- 特征:改变后仍是同类结果,只是满足程度不同
-
-## 输出格式
-
-词语[本质-描述]、词语[硬-描述]、词语[软-描述]
-
-## 注意
-- 只输出标注后的字符串
-- 结合需求背景判断意图
-""".strip()
-
-question_annotator = Agent[None](
-    name="问题标注专家",
-    instructions=question_annotation_instructions,
-    model=get_model(),
-)
-
-
-# ============================================================================
-# Agent 2: 分词专家
-# ============================================================================
-segmentation_instructions = """
-你是中文分词专家。给定一个句子,将其分词。
-
-## 分词原则
-
-1. 去掉标点符号
-2. 拆分成最小的有意义单元
-3. 去掉助词、语气词、助动词
-4. 保留疑问词
-5. 保留实词:名词、动词、形容词、副词
-
-## 输出要求
-
-输出分词列表。
-""".strip()
-
-class SegmentationResult(BaseModel):
-    """分词结果"""
-    words: list[str] = Field(..., description="分词列表")
-    reasoning: str = Field(..., description="分词说明")
-
-segmenter = Agent[None](
-    name="分词专家",
-    instructions=segmentation_instructions,
-    model=get_model(),
-    output_type=SegmentationResult,
-)
-
-
-# ============================================================================
-# Agent 3: 评估专家(意图匹配 + 相关性评分)
-# ============================================================================
-eval_instructions = """
-你是搜索query评估专家。给定原始问题、问题标注和推荐query,评估两个维度。
-
-## 输入信息
-
-你会收到:
-1. 原始问题:用户的原始表述
-2. 问题标注:对原始问题的三层标注(本质、硬性、软性)
-3. 推荐query:待评估的推荐词
-
-## 评估目标
-
-用这个推荐query搜索,能否找到满足原始需求的内容?
-
-## 两层评分
-
-### 1. intent_match(意图匹配)= true/false
-
-推荐query的**使用意图**是否与原问题的**本质**一致?
-
-**核心:只关注[本质]标注**
-- 问题标注中的 `[本质-XXX]` 标记明确了用户的核心意图
-- 判断推荐词是否能达成这个核心意图
-
-**常见本质类型:**
-- 找方法/如何获取 → 推荐词应包含方法、途径、网站、渠道等
-- 找教程 → 推荐词应是教程、教学相关
-- 找资源/素材 → 推荐词应是资源、素材本身
-- 找工具 → 推荐词应是工具推荐
-- 看作品 → 推荐词应是作品展示
-
-**评分:**
-- true = 推荐词的意图与 `[本质]` 一致
-- false = 推荐词的意图与 `[本质]` 不一致
-
-### 2. relevance_score(相关性)= 0-1 连续分数
-
-在意图匹配的前提下,推荐query在**主题、要素、属性**上与原问题的相关程度?
-
-**评估维度:**
-- 主题相关:核心主题是否匹配?(如:摄影、旅游、美食)
-- 要素覆盖:`[硬-XXX]` 标记的硬性约束保留了多少?(地域、时间、对象、工具等)
-- 属性匹配:`[软-XXX]` 标记的软性修饰保留了多少?(质量、速度、美观等)
-
-**评分参考:**
-- 0.9-1.0 = 几乎完美匹配,[硬]和[软]标注的要素都保留
-- 0.7-0.8 = 高度相关,[硬]标注的要素都保留,[软]标注少数缺失
-- 0.5-0.6 = 中度相关,[硬]标注的要素保留大部分,[软]标注多数缺失
-- 0.3-0.4 = 低度相关,[硬]标注的要素部分缺失
-- 0-0.2 = 基本不相关,[硬]标注的要素大量缺失
-
-## 评估策略
-
-1. **先看[本质]判断 intent_match**:意图不匹配直接 false
-2. **再看[硬][软]评估 relevance_score**:计算要素和属性的保留程度
-
-## 输出要求
-
-请先思考,再打分。按以下顺序输出:
-
-1. reason: 详细的评估理由(先分析再打分)
-   - 原问题的[本质]是什么,推荐词是否匹配这个本质
-   - [硬]约束哪些保留/缺失
-   - [软]修饰哪些保留/缺失
-   - 基于以上分析,给出意图匹配判断和相关性分数的依据
-
-2. intent_match: true/false(基于上述分析得出)
-
-3. relevance_score: 0-1 的浮点数(基于上述分析得出)
-""".strip()
-
-class RelevanceEvaluation(BaseModel):
-    """评估反馈模型 - 意图匹配 + 相关性"""
-    reason: str = Field(..., description="评估理由,需说明意图判断和相关性依据")
-    intent_match: bool = Field(..., description="意图是否匹配")
-    relevance_score: float = Field(..., description="相关性分数 0-1,分数越高越相关")
-
-evaluator = Agent[None](
-    name="评估专家",
-    instructions=eval_instructions,
-    model=get_model(),
-    output_type=RelevanceEvaluation,
-)
-
-
-# ============================================================================
-# 核心函数
-# ============================================================================
-
-async def annotate_question(q_with_context: str) -> str:
-    """标注问题(三层)"""
-    print("\n正在标注问题...")
-    result = await Runner.run(question_annotator, q_with_context)
-    annotation = str(result.final_output)
-    print(f"问题标注完成:{annotation}")
-    return annotation
-
-
-async def segment_text(q: str) -> SegmentationResult:
-    """分词"""
-    print("\n正在分词...")
-    result = await Runner.run(segmenter, q)
-    seg_result: SegmentationResult = result.final_output
-    print(f"分词结果:{seg_result.words}")
-    print(f"分词说明:{seg_result.reasoning}")
-    return seg_result
-
-
-def generate_query_combinations_single_level(
-    keywords: list[str],
-    size: int,
-    must_include_keywords: set[str] | None = None
-) -> list[str]:
-    """
-    生成单个层级的query组合
-
-    Args:
-        keywords: 关键词列表
-        size: 组合词数
-        must_include_keywords: 必须包含的关键词集合(用于剪枝)
-
-    Returns:
-        该层级的所有query组合
-    """
-    if size > len(keywords):
-        return []
-
-    # 1-word组合:不需要考虑顺序
-    if size == 1:
-        if must_include_keywords:
-            # 只返回必须包含的关键词
-            return [kw for kw in keywords if kw in must_include_keywords]
-        return keywords.copy()
-
-    # 多词组合:先选择size个词(combinations),再排列(permutations)
-    all_queries = []
-    combs = list(combinations(keywords, size))
-    for comb in combs:
-        # 如果设置了必须包含的关键词,检查组合是否包含至少一个
-        if must_include_keywords:
-            if not any(kw in must_include_keywords for kw in comb):
-                continue  # 跳过不包含必须关键词的组合
-
-        # 对每个组合生成所有排列
-        perms = list(permutations(comb))
-        for perm in perms:
-            query = ''.join(perm)  # 直接拼接,无空格
-            all_queries.append(query)
-
-    # 去重
-    return list(dict.fromkeys(all_queries))
-
-
-async def evaluate_single_sug_with_semaphore(
-    source_query: str,
-    sug_query: str,
-    original_question: str,
-    question_annotation: str,
-    semaphore: asyncio.Semaphore
-) -> dict:
-    """带信号量的单个推荐词评估"""
-    async with semaphore:
-        eval_input = f"""
-<原始问题>
-{original_question}
-</原始问题>
-
-<问题标注(三层)>
-{question_annotation}
-</问题标注(三层)>
-
-<待评估的推荐query>
-{sug_query}
-</待评估的推荐query>
-
-请评估该推荐query(请先分析理由,再给出评分):
-1. reason: 详细的评估理由(先思考分析)
-2. intent_match: 意图是否匹配(true/false)
-3. relevance_score: 相关性分数(0-1)
-
-评估时请参考问题标注中的[本质]、[硬]、[软]标记。
-"""
-        result = await Runner.run(evaluator, eval_input)
-        evaluation: RelevanceEvaluation = result.final_output
-        return {
-            "source_query": source_query,
-            "sug_query": sug_query,
-            "intent_match": evaluation.intent_match,
-            "relevance_score": evaluation.relevance_score,
-            "reason": evaluation.reason,
-        }
-
-
-async def fetch_and_evaluate_level(
-    queries: list[str],
-    original_question: str,
-    question_annotation: str,
-    level_name: str,
-    context: RunContext
-) -> tuple[list[dict], list[dict]]:
-    """
-    处理单个层级:获取推荐词并评估
-
-    Returns:
-        (sug_results, evaluations)
-    """
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-
-    # 创建信号量
-    api_semaphore = asyncio.Semaphore(API_CONCURRENCY_LIMIT)
-    model_semaphore = asyncio.Semaphore(MODEL_CONCURRENCY_LIMIT)
-
-    # 结果收集
-    sug_results = []
-    all_evaluations = []
-
-    # 统计
-    total_queries = len(queries)
-    completed_queries = 0
-    total_sugs = 0
-    completed_evals = 0
-
-    async def get_and_evaluate_single_query(query: str):
-        nonlocal completed_queries, total_sugs, completed_evals
-
-        # 步骤1:获取推荐词
-        async with api_semaphore:
-            suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-            sug_count = len(suggestions) if suggestions else 0
-
-            completed_queries += 1
-            total_sugs += sug_count
-
-            print(f"  [{completed_queries}/{total_queries}] {query} → {sug_count} 个推荐词")
-
-            sug_result = {
-                "query": query,
-                "suggestions": suggestions or [],
-                "timestamp": datetime.now().isoformat()
-            }
-            sug_results.append(sug_result)
-
-        # 步骤2:立即评估这些推荐词
-        if suggestions:
-            eval_tasks = []
-            for sug in suggestions:
-                eval_tasks.append(evaluate_single_sug_with_semaphore(
-                    query, sug, original_question, question_annotation, model_semaphore
-                ))
-
-            if eval_tasks:
-                evals = await asyncio.gather(*eval_tasks)
-                all_evaluations.extend(evals)
-                completed_evals += len(evals)
-                print(f"      ↳ 已评估 {len(evals)} 个,累计评估 {completed_evals} 个")
-
-    # 并发处理所有query
-    await asyncio.gather(*[get_and_evaluate_single_query(q) for q in queries])
-
-    print(f"\n{level_name} 完成:获取 {total_sugs} 个推荐词,完成 {completed_evals} 个评估")
-
-    return sug_results, all_evaluations
-
-
-def find_intent_matched_keywords(
-    keywords: list[str],
-    evaluations: list[dict]
-) -> set[str]:
-    """
-    找出所有至少有一个 intent_match=True 的推荐词的关键词
-
-    Args:
-        keywords: 当前层级使用的关键词列表
-        evaluations: 该层级的评估结果
-
-    Returns:
-        有意图匹配的关键词集合
-    """
-    matched_keywords = set()
-
-    for keyword in keywords:
-        # 检查这个关键词对应的推荐词中是否有 intent_match=True 的
-        keyword_evals = [
-            e for e in evaluations
-            if e['source_query'] == keyword and e['intent_match'] is True
-        ]
-
-        if keyword_evals:
-            matched_keywords.add(keyword)
-
-    return matched_keywords
-
-
-def find_top_keywords_by_relevance(
-    keywords: list[str],
-    evaluations: list[dict],
-    top_n: int = 2
-) -> list[str]:
-    """
-    根据 relevance_score 找出表现最好的 top N 关键词
-
-    Args:
-        keywords: 当前层级使用的关键词列表
-        evaluations: 该层级的评估结果
-        top_n: 保留的关键词数量
-
-    Returns:
-        按平均 relevance_score 排序的 top N 关键词
-    """
-    keyword_scores = {}
-
-    for keyword in keywords:
-        # 找到这个关键词对应的所有评估
-        keyword_evals = [
-            e for e in evaluations
-            if e['source_query'] == keyword
-        ]
-
-        if keyword_evals:
-            # 计算平均 relevance_score
-            avg_score = sum(e['relevance_score'] for e in keyword_evals) / len(keyword_evals)
-            # 同时记录最高分,作为次要排序依据
-            max_score = max(e['relevance_score'] for e in keyword_evals)
-            keyword_scores[keyword] = {
-                'avg': avg_score,
-                'max': max_score,
-                'count': len(keyword_evals)
-            }
-
-    if not keyword_scores:
-        return []
-
-    # 按平均分降序,最高分降序
-    sorted_keywords = sorted(
-        keyword_scores.items(),
-        key=lambda x: (x[1]['avg'], x[1]['max']),
-        reverse=True
-    )
-
-    # 返回 top N 关键词
-    return [kw for kw, score in sorted_keywords[:top_n]]
-
-
-def find_qualified_queries(evaluations: list[dict], min_relevance_score: float = 0.7) -> list[dict]:
-    """
-    查找所有合格的query
-
-    筛选标准:
-    1. intent_match = True(必须满足)
-    2. relevance_score >= min_relevance_score
-
-    返回:按 relevance_score 降序排列
-    """
-    qualified = [
-        e for e in evaluations
-        if e['intent_match'] is True and e['relevance_score'] >= min_relevance_score
-    ]
-
-    # 按relevance_score降序排列
-    return sorted(qualified, key=lambda x: x['relevance_score'], reverse=True)
-
-
-# ============================================================================
-# 主流程 - v6.4 层级剪枝
-# ============================================================================
-
-async def combinatorial_search_with_pruning(
-    context: RunContext,
-    max_combination_size: int = 1,
-    fallback_top_n: int = 2,
-    pruning_start_level: int = 3
-) -> dict:
-    """
-    组合式搜索流程(带层级剪枝)
-
-    策略:
-    - 第1-2层:充分探索,使用所有关键词
-    - 第3层及以上:开始剪枝
-      1. 优先使用在第1层中至少有一个 intent_match=True 的关键词
-      2. 如果没有,则使用 relevance_score 最高的 top N 关键词
-      3. 如果也无法计算,则使用全部关键词
-
-    Args:
-        context: 运行上下文
-        max_combination_size: 最大组合词数(N),默认1
-        fallback_top_n: 当没有意图匹配时,使用 relevance_score top N 关键词,默认2
-        pruning_start_level: 从第几层开始剪枝,默认3(即第1-2层充分探索)
-
-    返回格式:
-    {
-        "success": True/False,
-        "results": [...],
-        "message": "..."
-    }
-    """
-
-    # 步骤1:标注问题(三层)
-    annotation = await annotate_question(context.q_with_context)
-    context.question_annotation = annotation
-
-    # 步骤2:分词
-    seg_result = await segment_text(context.q)
-    all_keywords = seg_result.words
-    context.keywords = all_keywords
-
-    # 初始化累积结果
-    all_sug_results = []
-    all_evaluations = []
-
-    # 保存第1层的评估结果,用于后续剪枝
-    level_1_evaluations = []
-
-    # 保存第1层中有意图匹配的关键词(用于剪枝)
-    must_include_keywords = None
-
-    print(f"\n{'='*60}")
-    print(f"层级剪枝式搜索(最大层级:{max_combination_size},第{pruning_start_level}层开始剪枝)")
-    print(f"{'='*60}")
-
-    # 逐层处理
-    for level in range(1, max_combination_size + 1):
-        level_name = f"{level}-word"
-
-        print(f"\n{'='*60}")
-        print(f"第 {level} 层:{level_name}")
-        print(f"{'='*60}")
-
-        # 判断当前层是否需要剪枝
-        should_prune = level >= pruning_start_level and must_include_keywords is not None
-
-        if should_prune:
-            print(f"剪枝模式:只生成包含以下关键词之一的组合")
-            print(f"  必须包含的关键词:{sorted(must_include_keywords)}")
-
-        # 生成当前层的query组合
-        level_queries = generate_query_combinations_single_level(
-            all_keywords,
-            level,
-            must_include_keywords=must_include_keywords if should_prune else None
-        )
-
-        if not level_queries:
-            print(f"⚠️  无法生成 {level} 词组合,跳过")
-            context.pruning_info[level_name] = {
-                "available_keywords": list(all_keywords),
-                "queries_count": 0,
-                "pruned": should_prune,
-                "reason": f"剪枝后无有效组合" if should_prune else f"关键词数量不足以生成 {level} 词组合"
-            }
-            break
-
-        print(f"可用关键词:{all_keywords}")
-        if should_prune:
-            total_possible = len(generate_query_combinations_single_level(all_keywords, level))
-            print(f"剪枝效果:{len(level_queries)}/{total_possible} (保留 {len(level_queries)/total_possible*100:.1f}%)")
-        print(f"生成的query数:{len(level_queries)}")
-
-        # 记录该层的query组合
-        context.query_combinations[level_name] = level_queries
-
-        # 打印部分query示例
-        print(f"\nquery示例(前10个):")
-        for i, q in enumerate(level_queries[:10], 1):
-            print(f"  {i}. {q}")
-        if len(level_queries) > 10:
-            print(f"  ... 还有 {len(level_queries) - 10} 个")
-
-        # 获取推荐词并评估
-        print(f"\n开始处理第 {level} 层的推荐词...")
-        level_sug_results, level_evaluations = await fetch_and_evaluate_level(
-            level_queries,
-            context.q,
-            annotation,
-            level_name,
-            context
-        )
-
-        # 累积结果
-        all_sug_results.extend(level_sug_results)
-        all_evaluations.extend(level_evaluations)
-
-        # 保存第1层的评估结果
-        if level == 1:
-            level_1_evaluations = level_evaluations.copy()
-
-        # 统计该层的意图匹配情况
-        intent_matched_count = sum(1 for e in level_evaluations if e['intent_match'] is True)
-
-        print(f"\n第 {level} 层统计:")
-        print(f"  - 查询数:{len(level_queries)}")
-        print(f"  - 推荐词数:{sum(len(r['suggestions']) for r in level_sug_results)}")
-        print(f"  - 意图匹配数:{intent_matched_count}/{len(level_evaluations)}")
-
-        # 记录剪枝信息
-        context.pruning_info[level_name] = {
-            "available_keywords": list(all_keywords),
-            "must_include_keywords": list(must_include_keywords) if must_include_keywords else None,
-            "queries_count": len(level_queries),
-            "pruned": should_prune,
-            "intent_matched_count": intent_matched_count,
-            "total_evaluations": len(level_evaluations)
-        }
-
-        # 如果是第1层,准备剪枝关键词
-        if level == 1 and pruning_start_level > 1:
-            # 基于第1层的结果确定后续层必须包含的关键词
-            matched_keywords = find_intent_matched_keywords(all_keywords, level_1_evaluations)
-
-            print(f"\n准备剪枝策略(将用于第{pruning_start_level}层及以上):")
-            print(f"  - 原始关键词数:{len(all_keywords)}")
-            print(f"  - 意图匹配关键词数(基于第1层):{len(matched_keywords)}")
-
-            if matched_keywords:
-                print(f"  ✓ 后续层将只保留包含以下关键词之一的组合:{sorted(matched_keywords)}")
-                must_include_keywords = matched_keywords
-            else:
-                print(f"  ⚠️  第1层没有任何关键词产生 intent_match=True 的推荐词")
-                # 退而求其次:使用 relevance_score 最高的 top N 关键词
-                top_keywords = find_top_keywords_by_relevance(all_keywords, level_1_evaluations, top_n=fallback_top_n)
-
-                if top_keywords:
-                    print(f"  ✓ 后续层将只保留包含以下关键词之一的组合(基于 relevance top {fallback_top_n}):{top_keywords}")
-                    must_include_keywords = set(top_keywords)
-
-                    # 显示关键词的得分详情
-                    for kw in top_keywords:
-                        kw_evals = [e for e in level_1_evaluations if e['source_query'] == kw]
-                        if kw_evals:
-                            avg_score = sum(e['relevance_score'] for e in kw_evals) / len(kw_evals)
-                            max_score = max(e['relevance_score'] for e in kw_evals)
-                            print(f"    - {kw}: 平均={avg_score:.2f}, 最高={max_score:.2f}, 推荐词数={len(kw_evals)}")
-                else:
-                    print(f"  ⚠️  无法计算 relevance_score,后续层将不剪枝")
-                    must_include_keywords = None
-
-    # 保存累积结果
-    context.all_sug_queries = all_sug_results
-    context.evaluation_results = all_evaluations
-
-    # 筛选合格query
-    print(f"\n{'='*60}")
-    print(f"筛选最终结果")
-    print(f"{'='*60}")
-
-    qualified = find_qualified_queries(all_evaluations, min_relevance_score=0.7)
-
-    if qualified:
-        return {
-            "success": True,
-            "results": qualified,
-            "message": f"找到 {len(qualified)} 个合格query(intent_match=True 且 relevance>=0.7)"
-        }
-
-    # 降低标准
-    acceptable = find_qualified_queries(all_evaluations, min_relevance_score=0.5)
-    if acceptable:
-        return {
-            "success": True,
-            "results": acceptable,
-            "message": f"找到 {len(acceptable)} 个可接受query(intent_match=True 且 relevance>=0.5)"
-        }
-
-    # 完全失败:返回所有intent_match=True的
-    intent_matched = [e for e in all_evaluations if e['intent_match'] is True]
-    if intent_matched:
-        intent_matched_sorted = sorted(intent_matched, key=lambda x: x['relevance_score'], reverse=True)
-        return {
-            "success": False,
-            "results": intent_matched_sorted[:10],  # 只返回前10个
-            "message": f"未找到高相关性query,但有 {len(intent_matched)} 个意图匹配的推荐词"
-        }
-
-    return {
-        "success": False,
-        "results": [],
-        "message": "未找到任何意图匹配的推荐词"
-    }
-
-
-# ============================================================================
-# 输出格式化
-# ============================================================================
-
-def format_output(optimization_result: dict, context: RunContext) -> str:
-    """格式化输出结果"""
-    results = optimization_result.get("results", [])
-
-    output = f"原始问题:{context.q}\n"
-    output += f"问题标注:{context.question_annotation}\n"
-    output += f"提取的关键词:{', '.join(context.keywords or [])}\n"
-    output += f"关键词数量:{len(context.keywords or [])}\n"
-
-    # 层级剪枝信息
-    output += f"\n{'='*60}\n"
-    output += f"层级剪枝信息:\n"
-    output += f"{'='*60}\n"
-    for level_name, info in context.pruning_info.items():
-        output += f"\n{level_name}:\n"
-        if info.get('pruned'):
-            if info.get('reason'):
-                output += f"  状态:剪枝失败 ⚠️\n"
-                output += f"  原因:{info.get('reason')}\n"
-            else:
-                output += f"  状态:已剪枝 ✂️\n"
-                output += f"  必须包含关键词:{info.get('must_include_keywords', [])}\n"
-                output += f"  生成query数:{info['queries_count']}\n"
-        else:
-            output += f"  状态:充分探索 ✓\n"
-            output += f"  可用关键词数:{len(info['available_keywords'])}\n"
-            output += f"  可用关键词:{info['available_keywords']}\n"
-            output += f"  生成query数:{info['queries_count']}\n"
-
-        output += f"  意图匹配数:{info.get('intent_matched_count', 0)}/{info.get('total_evaluations', 0)}\n"
-
-    # query组合统计
-    output += f"\n{'='*60}\n"
-    output += f"query组合统计:\n"
-    output += f"{'='*60}\n"
-    for level, queries in context.query_combinations.items():
-        output += f"  - {level}: {len(queries)} 个\n"
-
-    # 统计信息
-    total_queries = sum(len(q) for q in context.query_combinations.values())
-    total_sugs = sum(len(r["suggestions"]) for r in context.all_sug_queries)
-    total_evals = len(context.evaluation_results)
-
-    output += f"\n探索统计:\n"
-    output += f"  - 总query数:{total_queries}\n"
-    output += f"  - 总推荐词数:{total_sugs}\n"
-    output += f"  - 总评估数:{total_evals}\n"
-
-    output += f"\n状态:{optimization_result['message']}\n\n"
-
-    if optimization_result["success"] and results:
-        output += "=" * 60 + "\n"
-        output += "合格的推荐query(按relevance_score降序):\n"
-        output += "=" * 60 + "\n"
-        for i, result in enumerate(results[:20], 1):  # 只显示前20个
-            output += f"\n{i}. [{result['relevance_score']:.2f}] {result['sug_query']}\n"
-            output += f"   来源:{result['source_query']}\n"
-            output += f"   意图:{'✓ 匹配' if result['intent_match'] else '✗ 不匹配'}\n"
-            output += f"   理由:{result['reason'][:150]}...\n" if len(result['reason']) > 150 else f"   理由:{result['reason']}\n"
-    else:
-        output += "=" * 60 + "\n"
-        output += "结果:未找到足够相关的推荐query\n"
-        output += "=" * 60 + "\n"
-        if results:
-            output += "\n最接近的推荐词(前10个):\n\n"
-            for i, result in enumerate(results[:10], 1):
-                output += f"{i}. [{result['relevance_score']:.2f}] {result['sug_query']}\n"
-                output += f"   来源:{result['source_query']}\n"
-                output += f"   意图:{'✓ 匹配' if result['intent_match'] else '✗ 不匹配'}\n\n"
-
-        # 按source_query分组显示
-        output += "\n" + "=" * 60 + "\n"
-        output += "按查询词分组的推荐词情况:\n"
-        output += "=" * 60 + "\n"
-
-        for sug_data in context.all_sug_queries:
-            source_q = sug_data["query"]
-            sugs = sug_data["suggestions"]
-
-            # 找到这个source_query对应的所有评估
-            related_evals = [e for e in context.evaluation_results if e["source_query"] == source_q]
-            intent_match_count = sum(1 for e in related_evals if e["intent_match"])
-            avg_relevance = sum(e["relevance_score"] for e in related_evals) / len(related_evals) if related_evals else 0
-
-            output += f"\n查询:{source_q}\n"
-            output += f"  推荐词数:{len(sugs)}\n"
-            output += f"  意图匹配数:{intent_match_count}/{len(related_evals)}\n"
-            output += f"  平均相关性:{avg_relevance:.2f}\n"
-
-            # 显示前3个推荐词
-            if sugs:
-                output += f"  示例推荐词:\n"
-                for sug in sugs[:3]:
-                    eval_item = next((e for e in related_evals if e["sug_query"] == sug), None)
-                    if eval_item:
-                        output += f"    - {sug} [意图:{'✓' if eval_item['intent_match'] else '✗'}, 相关:{eval_item['relevance_score']:.2f}]\n"
-                    else:
-                        output += f"    - {sug}\n"
-
-    return output.strip()
-
-
-# ============================================================================
-# 主函数
-# ============================================================================
-
-async def main(
-    input_dir: str,
-    max_combination_size: int = 1,
-    api_concurrency: int = API_CONCURRENCY_LIMIT,
-    model_concurrency: int = MODEL_CONCURRENCY_LIMIT,
-    fallback_top_n: int = 2,
-    pruning_start_level: int = 3
-):
-    # 更新全局并发配置
-    global API_CONCURRENCY_LIMIT, MODEL_CONCURRENCY_LIMIT
-    API_CONCURRENCY_LIMIT = api_concurrency
-    MODEL_CONCURRENCY_LIMIT = model_concurrency
-
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]
-
-    # 日志保存目录
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-
-    print(f"\n{'='*60}")
-    print(f"并发配置")
-    print(f"{'='*60}")
-    print(f"API请求并发度:{API_CONCURRENCY_LIMIT}")
-    print(f"模型评估并发度:{MODEL_CONCURRENCY_LIMIT}")
-
-    # 执行层级剪枝式搜索
-    optimization_result = await combinatorial_search_with_pruning(
-        run_context,
-        max_combination_size=max_combination_size,
-        fallback_top_n=fallback_top_n,
-        pruning_start_level=pruning_start_level
-    )
-
-    # 格式化输出
-    final_output = format_output(optimization_result, run_context)
-    print(f"\n{'='*60}")
-    print("最终结果")
-    print(f"{'='*60}")
-    print(final_output)
-
-    # 保存结果
-    run_context.optimization_result = optimization_result
-    run_context.final_output = final_output
-
-    # 保存 RunContext 到 log_dir
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(
-        description="搜索query优化工具 - v6.4 层级剪枝版",
-        formatter_class=argparse.RawDescriptionHelpFormatter,
-        epilog="""
-示例:
-  # 默认参数(只搜索1层)
-  python sug_v6_4_with_annotation.py
-
-  # 2层搜索,第2层只使用第1层中有意图匹配的关键词
-  python sug_v6_4_with_annotation.py --max-combo 2
-
-  # 2层搜索,如果第1层没有意图匹配,则使用 top 3 关键词
-  python sug_v6_4_with_annotation.py --max-combo 2 --fallback-top 3
-
-  # 3层搜索,API并发5,模型并发20
-  python sug_v6_4_with_annotation.py --max-combo 3 --api-concurrency 5 --model-concurrency 20
-
-  # 指定输入目录
-  python sug_v6_4_with_annotation.py --input-dir "input/旅游-逸趣玩旅行/如何获取能体现川西秋季特色的高质量风光摄影素材?"
-        """
-    )
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    parser.add_argument(
-        "--max-combo",
-        type=int,
-        default=1,
-        help="最大组合词数(N),默认: 1"
-    )
-    parser.add_argument(
-        "--fallback-top",
-        type=int,
-        default=2,
-        help="当第1层没有意图匹配时,使用 relevance_score top N 关键词,默认: 2"
-    )
-    parser.add_argument(
-        "--pruning-start",
-        type=int,
-        default=3,
-        help="从第几层开始剪枝,默认: 3(即第1-2层充分探索,第3层及以上剪枝)"
-    )
-    parser.add_argument(
-        "--api-concurrency",
-        type=int,
-        default=API_CONCURRENCY_LIMIT,
-        help=f"API请求并发度,默认: {API_CONCURRENCY_LIMIT}"
-    )
-    parser.add_argument(
-        "--model-concurrency",
-        type=int,
-        default=MODEL_CONCURRENCY_LIMIT,
-        help=f"模型评估并发度,默认: {MODEL_CONCURRENCY_LIMIT}"
-    )
-    args = parser.parse_args()
-
-    asyncio.run(main(
-        args.input_dir,
-        max_combination_size=args.max_combo,
-        api_concurrency=args.api_concurrency,
-        model_concurrency=args.model_concurrency,
-        fallback_top_n=args.fallback_top,
-        pruning_start_level=args.pruning_start
-    ))

+ 0 - 984
sug_v6_5_with_annotation.py

@@ -1,984 +0,0 @@
-import asyncio
-import json
-import os
-import argparse
-from datetime import datetime
-from itertools import combinations, permutations
-
-from agents import Agent, Runner
-from lib.my_trace import set_trace
-from typing import Literal
-from pydantic import BaseModel, Field
-
-from lib.utils import read_file_as_string
-from lib.client import get_model
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-
-# ============================================================================
-# 并发控制配置
-# ============================================================================
-# API请求并发度(小红书接口)
-API_CONCURRENCY_LIMIT = 5
-
-# 模型评估并发度(GPT评估)
-MODEL_CONCURRENCY_LIMIT = 10
-
-
-class RunContext(BaseModel):
-    version: str = Field(..., description="当前运行的脚本版本(文件名)")
-    input_files: dict[str, str] = Field(..., description="输入文件路径映射")
-    q_with_context: str
-    q_context: str
-    q: str
-    log_url: str
-    log_dir: str
-
-    # 问题标注
-    question_annotation: str | None = Field(default=None, description="问题的标注结果(三层)")
-
-    # 分词和组合
-    keywords: list[str] | None = Field(default=None, description="提取的关键词")
-    query_combinations: dict[str, list[str]] = Field(default_factory=dict, description="各层级的query组合")
-
-    # v6.4 新增:剪枝记录
-    pruning_info: dict[str, dict] = Field(default_factory=dict, description="各层级的剪枝信息")
-
-    # 探索结果
-    all_sug_queries: list[dict] = Field(default_factory=list, description="所有获取到的推荐词")
-
-    # 评估结果
-    evaluation_results: list[dict] = Field(default_factory=list, description="所有推荐词的评估结果")
-    optimization_result: dict | None = Field(default=None, description="最终优化结果对象")
-    final_output: str | None = Field(default=None, description="最终输出结果(格式化文本)")
-
-
-# ============================================================================
-# Agent 1: 问题标注专家
-# ============================================================================
-question_annotation_instructions = """
-你是搜索需求分析专家。给定问题(含需求背景),在原文上标注三层:本质、硬性、软性。
-
-## 判断标准
-
-**[本质]** - 问题的核心意图
-- 如何获取、教程、推荐、作品、测评等
-
-**[硬]** - 客观事实性约束(可明确验证、非主观判断)
-- 能明确区分类别的:地域、时间、对象、工具、操作类型
-- 特征:改变后得到完全不同类别的结果
-
-**[软]** - 主观判断性修饰(因人而异、程度性的)
-- 需要主观评价的:质量、速度、美观、特色、程度
-- 特征:改变后仍是同类结果,只是满足程度不同
-
-## 输出格式
-
-词语[本质-描述]、词语[硬-描述]、词语[软-描述]
-
-## 注意
-- 只输出标注后的字符串
-- 结合需求背景判断意图
-""".strip()
-
-question_annotator = Agent[None](
-    name="问题标注专家",
-    instructions=question_annotation_instructions,
-    model=get_model(),
-)
-
-
-# ============================================================================
-# Agent 2: 分词专家
-# ============================================================================
-segmentation_instructions = """
-你是中文分词专家。给定一个句子,将其分词。
-
-## 分词原则
-
-1. 去掉标点符号
-2. 拆分成最小的有意义单元
-3. 去掉助词、语气词、助动词
-4. 保留疑问词
-5. 保留实词:名词、动词、形容词、副词
-
-## 输出要求
-
-输出分词列表。
-""".strip()
-
-class SegmentationResult(BaseModel):
-    """分词结果"""
-    words: list[str] = Field(..., description="分词列表")
-    reasoning: str = Field(..., description="分词说明")
-
-segmenter = Agent[None](
-    name="分词专家",
-    instructions=segmentation_instructions,
-    model=get_model(),
-    output_type=SegmentationResult,
-)
-
-
-# ============================================================================
-# Agent 3: 批量评估专家(意图匹配 + 相关性评分)
-# ============================================================================
-batch_eval_instructions = """
-你是搜索query评估专家。给定原始问题、问题标注和一个源query的所有推荐词,批量评估这些推荐词。
-
-## 输入信息
-
-你会收到:
-1. 原始问题:用户的原始表述
-2. 问题标注:对原始问题的三层标注(本质、硬性、软性)
-3. 源query:用户输入的搜索词
-4. 推荐词列表:该源query返回的所有推荐词
-
-## 评估目标
-
-对每个推荐query,评估用它搜索能否找到满足原始需求的内容?
-
-## 两层评分
-
-### 1. intent_match(意图匹配)= true/false
-
-推荐query的**使用意图**是否与原问题的**本质**一致?
-
-**核心:只关注[本质]标注**
-- 问题标注中的 `[本质-XXX]` 标记明确了用户的核心意图
-- 判断推荐词是否能达成这个核心意图
-
-**常见本质类型:**
-- 找方法/如何获取 → 推荐词应包含方法、途径、网站、渠道等
-- 找教程 → 推荐词应是教程、教学相关
-- 找资源/素材 → 推荐词应是资源、素材本身
-- 找工具 → 推荐词应是工具推荐
-- 看作品 → 推荐词应是作品展示
-
-**评分:**
-- true = 推荐词的意图与 `[本质]` 一致
-- false = 推荐词的意图与 `[本质]` 不一致
-
-### 2. relevance_score(相关性)= 0-1 连续分数
-
-在意图匹配的前提下,推荐query在**主题、要素、属性**上与原问题的相关程度?
-
-**评估维度:**
-- 主题相关:核心主题是否匹配?(如:摄影、旅游、美食)
-- 要素覆盖:`[硬-XXX]` 标记的硬性约束保留了多少?(地域、时间、对象、工具等)
-- 属性匹配:`[软-XXX]` 标记的软性修饰保留了多少?(质量、速度、美观等)
-
-**评分参考:**
-- 0.9-1.0 = 几乎完美匹配,[硬]和[软]标注的要素都保留
-- 0.7-0.8 = 高度相关,[硬]标注的要素都保留,[软]标注少数缺失
-- 0.5-0.6 = 中度相关,[硬]标注的要素保留大部分,[软]标注多数缺失
-- 0.3-0.4 = 低度相关,[硬]标注的要素部分缺失
-- 0-0.2 = 基本不相关,[硬]标注的要素大量缺失
-
-## 评估策略
-
-1. **先整体浏览所有推荐词**:了解推荐词的分布和特点
-2. **逐个评估**:对每个推荐词先分析再打分
-3. **保持一致性**:使用相同的标准评估所有推荐词
-
-## 输出要求
-
-对每个推荐词,按以下顺序输出评估结果:
-
-1. reason: 详细的评估理由(先分析再打分)
-   - 原问题的[本质]是什么,推荐词是否匹配这个本质
-   - [硬]约束哪些保留/缺失
-   - [软]修饰哪些保留/缺失
-   - 基于以上分析,给出意图匹配判断和相关性分数的依据
-
-2. intent_match: true/false(基于上述分析得出)
-
-3. relevance_score: 0-1 的浮点数(基于上述分析得出)
-""".strip()
-
-class SingleQueryEvaluation(BaseModel):
-    """单个推荐词的评估结果"""
-    sug_query: str = Field(..., description="推荐词")
-    reason: str = Field(..., description="评估理由,需说明意图判断和相关性依据")
-    intent_match: bool = Field(..., description="意图是否匹配")
-    relevance_score: float = Field(..., description="相关性分数 0-1,分数越高越相关")
-
-class BatchEvaluationResult(BaseModel):
-    """批量评估结果"""
-    evaluations: list[SingleQueryEvaluation] = Field(..., description="所有推荐词的评估结果")
-
-batch_evaluator = Agent[None](
-    name="批量评估专家",
-    instructions=batch_eval_instructions,
-    model=get_model(),
-    output_type=BatchEvaluationResult,
-)
-
-
-# ============================================================================
-# 核心函数
-# ============================================================================
-
-async def annotate_question(q_with_context: str) -> str:
-    """标注问题(三层)"""
-    print("\n正在标注问题...")
-    result = await Runner.run(question_annotator, q_with_context)
-    annotation = str(result.final_output)
-    print(f"问题标注完成:{annotation}")
-    return annotation
-
-
-async def segment_text(q: str) -> SegmentationResult:
-    """分词"""
-    print("\n正在分词...")
-    result = await Runner.run(segmenter, q)
-    seg_result: SegmentationResult = result.final_output
-    print(f"分词结果:{seg_result.words}")
-    print(f"分词说明:{seg_result.reasoning}")
-    return seg_result
-
-
-def generate_query_combinations_single_level(
-    keywords: list[str],
-    size: int,
-    must_include_keywords: set[str] | None = None
-) -> list[str]:
-    """
-    生成单个层级的query组合
-
-    Args:
-        keywords: 关键词列表
-        size: 组合词数
-        must_include_keywords: 必须包含的关键词集合(用于剪枝)
-
-    Returns:
-        该层级的所有query组合
-    """
-    if size > len(keywords):
-        return []
-
-    # 1-word组合:不需要考虑顺序
-    if size == 1:
-        if must_include_keywords:
-            # 只返回必须包含的关键词
-            return [kw for kw in keywords if kw in must_include_keywords]
-        return keywords.copy()
-
-    # 多词组合:先选择size个词(combinations),再排列(permutations)
-    all_queries = []
-    combs = list(combinations(keywords, size))
-    for comb in combs:
-        # 如果设置了必须包含的关键词,检查组合是否包含至少一个
-        if must_include_keywords:
-            if not any(kw in must_include_keywords for kw in comb):
-                continue  # 跳过不包含必须关键词的组合
-
-        # 对每个组合生成所有排列
-        perms = list(permutations(comb))
-        for perm in perms:
-            query = ''.join(perm)  # 直接拼接,无空格
-            all_queries.append(query)
-
-    # 去重
-    return list(dict.fromkeys(all_queries))
-
-
-async def evaluate_batch_suggestions_with_semaphore(
-    source_query: str,
-    sug_queries: list[str],
-    original_question: str,
-    question_annotation: str,
-    semaphore: asyncio.Semaphore
-) -> list[dict]:
-    """带信号量的批量推荐词评估"""
-    if not sug_queries:
-        return []
-
-    async with semaphore:
-        # 格式化推荐词列表
-        sug_list_str = "\n".join([f"{i+1}. {sug}" for i, sug in enumerate(sug_queries)])
-
-        eval_input = f"""
-<原始问题>
-{original_question}
-</原始问题>
-
-<问题标注(三层)>
-{question_annotation}
-</问题标注(三层)>
-
-<源query>
-{source_query}
-</源query>
-
-<推荐词列表>
-{sug_list_str}
-</推荐词列表>
-
-请批量评估以上所有推荐词。对每个推荐词:
-1. reason: 详细的评估理由(先思考分析)
-2. intent_match: 意图是否匹配(true/false)
-3. relevance_score: 相关性分数(0-1)
-
-评估时请参考问题标注中的[本质]、[硬]、[软]标记。
-"""
-        result = await Runner.run(batch_evaluator, eval_input)
-        batch_result: BatchEvaluationResult = result.final_output
-
-        # 转换为统一格式
-        evaluations = []
-        for eval_item in batch_result.evaluations:
-            evaluations.append({
-                "source_query": source_query,
-                "sug_query": eval_item.sug_query,
-                "intent_match": eval_item.intent_match,
-                "relevance_score": eval_item.relevance_score,
-                "reason": eval_item.reason,
-            })
-
-        return evaluations
-
-
-async def fetch_and_evaluate_level(
-    queries: list[str],
-    original_question: str,
-    question_annotation: str,
-    level_name: str,
-    context: RunContext
-) -> tuple[list[dict], list[dict]]:
-    """
-    处理单个层级:获取推荐词并批量评估
-
-    Returns:
-        (sug_results, evaluations)
-    """
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-
-    # 创建信号量
-    api_semaphore = asyncio.Semaphore(API_CONCURRENCY_LIMIT)
-    model_semaphore = asyncio.Semaphore(MODEL_CONCURRENCY_LIMIT)
-
-    # 结果收集
-    sug_results = []
-    all_evaluations = []
-
-    # 统计
-    total_queries = len(queries)
-    completed_queries = 0
-    total_sugs = 0
-    completed_evals = 0
-
-    async def get_and_evaluate_single_query(query: str):
-        nonlocal completed_queries, total_sugs, completed_evals
-
-        # 步骤1:获取推荐词
-        async with api_semaphore:
-            suggestions = xiaohongshu_api.get_recommendations(keyword=query)
-            sug_count = len(suggestions) if suggestions else 0
-
-            completed_queries += 1
-            total_sugs += sug_count
-
-            print(f"  [{completed_queries}/{total_queries}] {query} → {sug_count} 个推荐词")
-
-            sug_result = {
-                "query": query,
-                "suggestions": suggestions or [],
-                "timestamp": datetime.now().isoformat()
-            }
-            sug_results.append(sug_result)
-
-        # 步骤2:批量评估这些推荐词
-        if suggestions:
-            evals = await evaluate_batch_suggestions_with_semaphore(
-                query, suggestions, original_question, question_annotation, model_semaphore
-            )
-            all_evaluations.extend(evals)
-            completed_evals += len(evals)
-            print(f"      ↳ 批量评估 {len(evals)} 个,累计评估 {completed_evals} 个")
-
-    # 并发处理所有query
-    await asyncio.gather(*[get_and_evaluate_single_query(q) for q in queries])
-
-    print(f"\n{level_name} 完成:获取 {total_sugs} 个推荐词,完成 {completed_evals} 个评估")
-
-    return sug_results, all_evaluations
-
-
-def find_intent_matched_keywords(
-    keywords: list[str],
-    evaluations: list[dict]
-) -> set[str]:
-    """
-    找出所有至少有一个 intent_match=True 的推荐词的关键词
-
-    Args:
-        keywords: 当前层级使用的关键词列表
-        evaluations: 该层级的评估结果
-
-    Returns:
-        有意图匹配的关键词集合
-    """
-    matched_keywords = set()
-
-    for keyword in keywords:
-        # 检查这个关键词对应的推荐词中是否有 intent_match=True 的
-        keyword_evals = [
-            e for e in evaluations
-            if e['source_query'] == keyword and e['intent_match'] is True
-        ]
-
-        if keyword_evals:
-            matched_keywords.add(keyword)
-
-    return matched_keywords
-
-
-def find_top_keywords_by_relevance(
-    keywords: list[str],
-    evaluations: list[dict],
-    top_n: int = 2
-) -> list[str]:
-    """
-    根据 relevance_score 找出表现最好的 top N 关键词
-
-    Args:
-        keywords: 当前层级使用的关键词列表
-        evaluations: 该层级的评估结果
-        top_n: 保留的关键词数量
-
-    Returns:
-        按平均 relevance_score 排序的 top N 关键词
-    """
-    keyword_scores = {}
-
-    for keyword in keywords:
-        # 找到这个关键词对应的所有评估
-        keyword_evals = [
-            e for e in evaluations
-            if e['source_query'] == keyword
-        ]
-
-        if keyword_evals:
-            # 计算平均 relevance_score
-            avg_score = sum(e['relevance_score'] for e in keyword_evals) / len(keyword_evals)
-            # 同时记录最高分,作为次要排序依据
-            max_score = max(e['relevance_score'] for e in keyword_evals)
-            keyword_scores[keyword] = {
-                'avg': avg_score,
-                'max': max_score,
-                'count': len(keyword_evals)
-            }
-
-    if not keyword_scores:
-        return []
-
-    # 按平均分降序,最高分降序
-    sorted_keywords = sorted(
-        keyword_scores.items(),
-        key=lambda x: (x[1]['avg'], x[1]['max']),
-        reverse=True
-    )
-
-    # 返回 top N 关键词
-    return [kw for kw, score in sorted_keywords[:top_n]]
-
-
-def find_qualified_queries(evaluations: list[dict], min_relevance_score: float = 0.7) -> list[dict]:
-    """
-    查找所有合格的query
-
-    筛选标准:
-    1. intent_match = True(必须满足)
-    2. relevance_score >= min_relevance_score
-
-    返回:按 relevance_score 降序排列
-    """
-    qualified = [
-        e for e in evaluations
-        if e['intent_match'] is True and e['relevance_score'] >= min_relevance_score
-    ]
-
-    # 按relevance_score降序排列
-    return sorted(qualified, key=lambda x: x['relevance_score'], reverse=True)
-
-
-# ============================================================================
-# 主流程 - v6.4 层级剪枝
-# ============================================================================
-
-async def combinatorial_search_with_pruning(
-    context: RunContext,
-    max_combination_size: int = 1,
-    fallback_top_n: int = 2,
-    pruning_start_level: int = 3
-) -> dict:
-    """
-    组合式搜索流程(带层级剪枝)
-
-    策略:
-    - 第1-2层:充分探索,使用所有关键词
-    - 第3层及以上:开始剪枝
-      1. 优先使用在第1层中至少有一个 intent_match=True 的关键词
-      2. 如果没有,则使用 relevance_score 最高的 top N 关键词
-      3. 如果也无法计算,则使用全部关键词
-
-    Args:
-        context: 运行上下文
-        max_combination_size: 最大组合词数(N),默认1
-        fallback_top_n: 当没有意图匹配时,使用 relevance_score top N 关键词,默认2
-        pruning_start_level: 从第几层开始剪枝,默认3(即第1-2层充分探索)
-
-    返回格式:
-    {
-        "success": True/False,
-        "results": [...],
-        "message": "..."
-    }
-    """
-
-    # 步骤1:标注问题(三层)
-    annotation = await annotate_question(context.q_with_context)
-    context.question_annotation = annotation
-
-    # 步骤2:分词
-    seg_result = await segment_text(context.q)
-    all_keywords = seg_result.words
-    context.keywords = all_keywords
-
-    # 初始化累积结果
-    all_sug_results = []
-    all_evaluations = []
-
-    # 保存第1层的评估结果,用于后续剪枝
-    level_1_evaluations = []
-
-    # 保存第1层中有意图匹配的关键词(用于剪枝)
-    must_include_keywords = None
-
-    print(f"\n{'='*60}")
-    print(f"层级剪枝式搜索(最大层级:{max_combination_size},第{pruning_start_level}层开始剪枝)")
-    print(f"{'='*60}")
-
-    # 逐层处理
-    for level in range(1, max_combination_size + 1):
-        level_name = f"{level}-word"
-
-        print(f"\n{'='*60}")
-        print(f"第 {level} 层:{level_name}")
-        print(f"{'='*60}")
-
-        # 判断当前层是否需要剪枝
-        should_prune = level >= pruning_start_level and must_include_keywords is not None
-
-        if should_prune:
-            print(f"剪枝模式:只生成包含以下关键词之一的组合")
-            print(f"  必须包含的关键词:{sorted(must_include_keywords)}")
-
-        # 生成当前层的query组合
-        level_queries = generate_query_combinations_single_level(
-            all_keywords,
-            level,
-            must_include_keywords=must_include_keywords if should_prune else None
-        )
-
-        if not level_queries:
-            print(f"⚠️  无法生成 {level} 词组合,跳过")
-            context.pruning_info[level_name] = {
-                "available_keywords": list(all_keywords),
-                "queries_count": 0,
-                "pruned": should_prune,
-                "reason": f"剪枝后无有效组合" if should_prune else f"关键词数量不足以生成 {level} 词组合"
-            }
-            break
-
-        print(f"可用关键词:{all_keywords}")
-        if should_prune:
-            total_possible = len(generate_query_combinations_single_level(all_keywords, level))
-            print(f"剪枝效果:{len(level_queries)}/{total_possible} (保留 {len(level_queries)/total_possible*100:.1f}%)")
-        print(f"生成的query数:{len(level_queries)}")
-
-        # 记录该层的query组合
-        context.query_combinations[level_name] = level_queries
-
-        # 打印部分query示例
-        print(f"\nquery示例(前10个):")
-        for i, q in enumerate(level_queries[:10], 1):
-            print(f"  {i}. {q}")
-        if len(level_queries) > 10:
-            print(f"  ... 还有 {len(level_queries) - 10} 个")
-
-        # 获取推荐词并评估
-        print(f"\n开始处理第 {level} 层的推荐词...")
-        level_sug_results, level_evaluations = await fetch_and_evaluate_level(
-            level_queries,
-            context.q,
-            annotation,
-            level_name,
-            context
-        )
-
-        # 累积结果
-        all_sug_results.extend(level_sug_results)
-        all_evaluations.extend(level_evaluations)
-
-        # 保存第1层的评估结果
-        if level == 1:
-            level_1_evaluations = level_evaluations.copy()
-
-        # 统计该层的意图匹配情况
-        intent_matched_count = sum(1 for e in level_evaluations if e['intent_match'] is True)
-
-        print(f"\n第 {level} 层统计:")
-        print(f"  - 查询数:{len(level_queries)}")
-        print(f"  - 推荐词数:{sum(len(r['suggestions']) for r in level_sug_results)}")
-        print(f"  - 意图匹配数:{intent_matched_count}/{len(level_evaluations)}")
-
-        # 记录剪枝信息
-        context.pruning_info[level_name] = {
-            "available_keywords": list(all_keywords),
-            "must_include_keywords": list(must_include_keywords) if must_include_keywords else None,
-            "queries_count": len(level_queries),
-            "pruned": should_prune,
-            "intent_matched_count": intent_matched_count,
-            "total_evaluations": len(level_evaluations)
-        }
-
-        # 如果是第1层,准备剪枝关键词
-        if level == 1 and pruning_start_level > 1:
-            # 基于第1层的结果确定后续层必须包含的关键词
-            matched_keywords = find_intent_matched_keywords(all_keywords, level_1_evaluations)
-
-            print(f"\n准备剪枝策略(将用于第{pruning_start_level}层及以上):")
-            print(f"  - 原始关键词数:{len(all_keywords)}")
-            print(f"  - 意图匹配关键词数(基于第1层):{len(matched_keywords)}")
-
-            if matched_keywords:
-                print(f"  ✓ 后续层将只保留包含以下关键词之一的组合:{sorted(matched_keywords)}")
-                must_include_keywords = matched_keywords
-            else:
-                print(f"  ⚠️  第1层没有任何关键词产生 intent_match=True 的推荐词")
-                # 退而求其次:使用 relevance_score 最高的 top N 关键词
-                top_keywords = find_top_keywords_by_relevance(all_keywords, level_1_evaluations, top_n=fallback_top_n)
-
-                if top_keywords:
-                    print(f"  ✓ 后续层将只保留包含以下关键词之一的组合(基于 relevance top {fallback_top_n}):{top_keywords}")
-                    must_include_keywords = set(top_keywords)
-
-                    # 显示关键词的得分详情
-                    for kw in top_keywords:
-                        kw_evals = [e for e in level_1_evaluations if e['source_query'] == kw]
-                        if kw_evals:
-                            avg_score = sum(e['relevance_score'] for e in kw_evals) / len(kw_evals)
-                            max_score = max(e['relevance_score'] for e in kw_evals)
-                            print(f"    - {kw}: 平均={avg_score:.2f}, 最高={max_score:.2f}, 推荐词数={len(kw_evals)}")
-                else:
-                    print(f"  ⚠️  无法计算 relevance_score,后续层将不剪枝")
-                    must_include_keywords = None
-
-    # 保存累积结果
-    context.all_sug_queries = all_sug_results
-    context.evaluation_results = all_evaluations
-
-    # 筛选合格query
-    print(f"\n{'='*60}")
-    print(f"筛选最终结果")
-    print(f"{'='*60}")
-
-    qualified = find_qualified_queries(all_evaluations, min_relevance_score=0.7)
-
-    if qualified:
-        return {
-            "success": True,
-            "results": qualified,
-            "message": f"找到 {len(qualified)} 个合格query(intent_match=True 且 relevance>=0.7)"
-        }
-
-    # 降低标准
-    acceptable = find_qualified_queries(all_evaluations, min_relevance_score=0.5)
-    if acceptable:
-        return {
-            "success": True,
-            "results": acceptable,
-            "message": f"找到 {len(acceptable)} 个可接受query(intent_match=True 且 relevance>=0.5)"
-        }
-
-    # 完全失败:返回所有intent_match=True的
-    intent_matched = [e for e in all_evaluations if e['intent_match'] is True]
-    if intent_matched:
-        intent_matched_sorted = sorted(intent_matched, key=lambda x: x['relevance_score'], reverse=True)
-        return {
-            "success": False,
-            "results": intent_matched_sorted[:10],  # 只返回前10个
-            "message": f"未找到高相关性query,但有 {len(intent_matched)} 个意图匹配的推荐词"
-        }
-
-    return {
-        "success": False,
-        "results": [],
-        "message": "未找到任何意图匹配的推荐词"
-    }
-
-
-# ============================================================================
-# 输出格式化
-# ============================================================================
-
-def format_output(optimization_result: dict, context: RunContext) -> str:
-    """格式化输出结果"""
-    results = optimization_result.get("results", [])
-
-    output = f"原始问题:{context.q}\n"
-    output += f"问题标注:{context.question_annotation}\n"
-    output += f"提取的关键词:{', '.join(context.keywords or [])}\n"
-    output += f"关键词数量:{len(context.keywords or [])}\n"
-
-    # 层级剪枝信息
-    output += f"\n{'='*60}\n"
-    output += f"层级剪枝信息:\n"
-    output += f"{'='*60}\n"
-    for level_name, info in context.pruning_info.items():
-        output += f"\n{level_name}:\n"
-        if info.get('pruned'):
-            if info.get('reason'):
-                output += f"  状态:剪枝失败 ⚠️\n"
-                output += f"  原因:{info.get('reason')}\n"
-            else:
-                output += f"  状态:已剪枝 ✂️\n"
-                output += f"  必须包含关键词:{info.get('must_include_keywords', [])}\n"
-                output += f"  生成query数:{info['queries_count']}\n"
-        else:
-            output += f"  状态:充分探索 ✓\n"
-            output += f"  可用关键词数:{len(info['available_keywords'])}\n"
-            output += f"  可用关键词:{info['available_keywords']}\n"
-            output += f"  生成query数:{info['queries_count']}\n"
-
-        output += f"  意图匹配数:{info.get('intent_matched_count', 0)}/{info.get('total_evaluations', 0)}\n"
-
-    # query组合统计
-    output += f"\n{'='*60}\n"
-    output += f"query组合统计:\n"
-    output += f"{'='*60}\n"
-    for level, queries in context.query_combinations.items():
-        output += f"  - {level}: {len(queries)} 个\n"
-
-    # 统计信息
-    total_queries = sum(len(q) for q in context.query_combinations.values())
-    total_sugs = sum(len(r["suggestions"]) for r in context.all_sug_queries)
-    total_evals = len(context.evaluation_results)
-
-    output += f"\n探索统计:\n"
-    output += f"  - 总query数:{total_queries}\n"
-    output += f"  - 总推荐词数:{total_sugs}\n"
-    output += f"  - 总评估数:{total_evals}\n"
-
-    output += f"\n状态:{optimization_result['message']}\n\n"
-
-    if optimization_result["success"] and results:
-        output += "=" * 60 + "\n"
-        output += "合格的推荐query(按relevance_score降序):\n"
-        output += "=" * 60 + "\n"
-        for i, result in enumerate(results[:20], 1):  # 只显示前20个
-            output += f"\n{i}. [{result['relevance_score']:.2f}] {result['sug_query']}\n"
-            output += f"   来源:{result['source_query']}\n"
-            output += f"   意图:{'✓ 匹配' if result['intent_match'] else '✗ 不匹配'}\n"
-            output += f"   理由:{result['reason'][:150]}...\n" if len(result['reason']) > 150 else f"   理由:{result['reason']}\n"
-    else:
-        output += "=" * 60 + "\n"
-        output += "结果:未找到足够相关的推荐query\n"
-        output += "=" * 60 + "\n"
-        if results:
-            output += "\n最接近的推荐词(前10个):\n\n"
-            for i, result in enumerate(results[:10], 1):
-                output += f"{i}. [{result['relevance_score']:.2f}] {result['sug_query']}\n"
-                output += f"   来源:{result['source_query']}\n"
-                output += f"   意图:{'✓ 匹配' if result['intent_match'] else '✗ 不匹配'}\n\n"
-
-        # 按source_query分组显示
-        output += "\n" + "=" * 60 + "\n"
-        output += "按查询词分组的推荐词情况:\n"
-        output += "=" * 60 + "\n"
-
-        for sug_data in context.all_sug_queries:
-            source_q = sug_data["query"]
-            sugs = sug_data["suggestions"]
-
-            # 找到这个source_query对应的所有评估
-            related_evals = [e for e in context.evaluation_results if e["source_query"] == source_q]
-            intent_match_count = sum(1 for e in related_evals if e["intent_match"])
-            avg_relevance = sum(e["relevance_score"] for e in related_evals) / len(related_evals) if related_evals else 0
-
-            output += f"\n查询:{source_q}\n"
-            output += f"  推荐词数:{len(sugs)}\n"
-            output += f"  意图匹配数:{intent_match_count}/{len(related_evals)}\n"
-            output += f"  平均相关性:{avg_relevance:.2f}\n"
-
-            # 显示前3个推荐词
-            if sugs:
-                output += f"  示例推荐词:\n"
-                for sug in sugs[:3]:
-                    eval_item = next((e for e in related_evals if e["sug_query"] == sug), None)
-                    if eval_item:
-                        output += f"    - {sug} [意图:{'✓' if eval_item['intent_match'] else '✗'}, 相关:{eval_item['relevance_score']:.2f}]\n"
-                    else:
-                        output += f"    - {sug}\n"
-
-    return output.strip()
-
-
-# ============================================================================
-# 主函数
-# ============================================================================
-
-async def main(
-    input_dir: str,
-    max_combination_size: int = 1,
-    api_concurrency: int = API_CONCURRENCY_LIMIT,
-    model_concurrency: int = MODEL_CONCURRENCY_LIMIT,
-    fallback_top_n: int = 2,
-    pruning_start_level: int = 3
-):
-    # 更新全局并发配置
-    global API_CONCURRENCY_LIMIT, MODEL_CONCURRENCY_LIMIT
-    API_CONCURRENCY_LIMIT = api_concurrency
-    MODEL_CONCURRENCY_LIMIT = model_concurrency
-
-    current_time, log_url = set_trace()
-
-    # 从目录中读取固定文件名
-    input_context_file = os.path.join(input_dir, 'context.md')
-    input_q_file = os.path.join(input_dir, 'q.md')
-
-    q_context = read_file_as_string(input_context_file)
-    q = read_file_as_string(input_q_file)
-    q_with_context = f"""
-<需求上下文>
-{q_context}
-</需求上下文>
-<当前问题>
-{q}
-</当前问题>
-""".strip()
-
-    # 获取当前文件名作为版本
-    version = os.path.basename(__file__)
-    version_name = os.path.splitext(version)[0]
-
-    # 日志保存目录
-    log_dir = os.path.join(input_dir, "output", version_name, current_time)
-
-    run_context = RunContext(
-        version=version,
-        input_files={
-            "input_dir": input_dir,
-            "context_file": input_context_file,
-            "q_file": input_q_file,
-        },
-        q_with_context=q_with_context,
-        q_context=q_context,
-        q=q,
-        log_dir=log_dir,
-        log_url=log_url,
-    )
-
-    print(f"\n{'='*60}")
-    print(f"并发配置")
-    print(f"{'='*60}")
-    print(f"API请求并发度:{API_CONCURRENCY_LIMIT}")
-    print(f"模型评估并发度:{MODEL_CONCURRENCY_LIMIT}")
-
-    # 执行层级剪枝式搜索
-    optimization_result = await combinatorial_search_with_pruning(
-        run_context,
-        max_combination_size=max_combination_size,
-        fallback_top_n=fallback_top_n,
-        pruning_start_level=pruning_start_level
-    )
-
-    # 格式化输出
-    final_output = format_output(optimization_result, run_context)
-    print(f"\n{'='*60}")
-    print("最终结果")
-    print(f"{'='*60}")
-    print(final_output)
-
-    # 保存结果
-    run_context.optimization_result = optimization_result
-    run_context.final_output = final_output
-
-    # 保存 RunContext 到 log_dir
-    os.makedirs(run_context.log_dir, exist_ok=True)
-    context_file_path = os.path.join(run_context.log_dir, "run_context.json")
-    with open(context_file_path, "w", encoding="utf-8") as f:
-        json.dump(run_context.model_dump(), f, ensure_ascii=False, indent=2)
-    print(f"\nRunContext saved to: {context_file_path}")
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(
-        description="搜索query优化工具 - v6.5 批量评估版",
-        formatter_class=argparse.RawDescriptionHelpFormatter,
-        epilog="""
-示例:
-  # 默认参数(只搜索1层)
-  python sug_v6_5_with_annotation.py
-
-  # 3层搜索,第3层开始剪枝
-  python sug_v6_5_with_annotation.py --max-combo 3
-
-  # 3层搜索,如果第1层没有意图匹配,则使用 top 3 关键词
-  python sug_v6_5_with_annotation.py --max-combo 3 --fallback-top 3
-
-  # 4层搜索,第4层才开始剪枝
-  python sug_v6_5_with_annotation.py --max-combo 4 --pruning-start 4
-
-  # 指定输入目录
-  python sug_v6_5_with_annotation.py --input-dir "input/旅游-逸趣玩旅行/如何获取能体现川西秋季特色的高质量风光摄影素材?"
-        """
-    )
-    parser.add_argument(
-        "--input-dir",
-        type=str,
-        default="input/简单扣图",
-        help="输入目录路径,默认: input/简单扣图"
-    )
-    parser.add_argument(
-        "--max-combo",
-        type=int,
-        default=1,
-        help="最大组合词数(N),默认: 1"
-    )
-    parser.add_argument(
-        "--fallback-top",
-        type=int,
-        default=2,
-        help="当第1层没有意图匹配时,使用 relevance_score top N 关键词,默认: 2"
-    )
-    parser.add_argument(
-        "--pruning-start",
-        type=int,
-        default=3,
-        help="从第几层开始剪枝,默认: 3(即第1-2层充分探索,第3层及以上剪枝)"
-    )
-    parser.add_argument(
-        "--api-concurrency",
-        type=int,
-        default=API_CONCURRENCY_LIMIT,
-        help=f"API请求并发度,默认: {API_CONCURRENCY_LIMIT}"
-    )
-    parser.add_argument(
-        "--model-concurrency",
-        type=int,
-        default=MODEL_CONCURRENCY_LIMIT,
-        help=f"模型评估并发度,默认: {MODEL_CONCURRENCY_LIMIT}"
-    )
-    args = parser.parse_args()
-
-    asyncio.run(main(
-        args.input_dir,
-        max_combination_size=args.max_combo,
-        api_concurrency=args.api_concurrency,
-        model_concurrency=args.model_concurrency,
-        fallback_top_n=args.fallback_top,
-        pruning_start_level=args.pruning_start
-    ))

+ 0 - 415
test.py

@@ -1,415 +0,0 @@
-import asyncio
-from typing import List
-
-from agents import Agent, Runner, function_tool
-from lib.my_trace import set_trace
-from lib.utils import read_file_as_string
-from script.search_recommendations.xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-@function_tool
-def get_query_suggestions(query: str):
-    """Fetch search recommendations from Xiaohongshu."""
-    xiaohongshu_api = XiaohongshuSearchRecommendations()
-    query_suggestions = xiaohongshu_api.get_recommendations(keyword=query)['result']['data']['data']
-    return query_suggestions
-
-@function_tool
-def evaluate_suggestions(original_problem: str, current_query: str, round_number: int, found_equivalent: bool, equivalent_query: str, evaluation_reason: str):
-    """
-    Record the evaluation result after analyzing suggestions from get_query_suggestions.
-
-    Args:
-        original_problem: The original problem from user input
-        current_query: The current query used to get these suggestions
-        round_number: Current round number (starting from 1)
-        found_equivalent: Whether an equivalent query was found in the suggestions (True/False)
-        equivalent_query: The equivalent query found (if found_equivalent=True), otherwise empty string ""
-        evaluation_reason: Detailed explanation of the evaluation result, including:
-            - If found: why the equivalent_query is semantically equivalent to original_problem
-            - If not found: what patterns were observed in suggestions and why none match
-
-    Returns:
-        A dict containing evaluation results
-    """
-    return {
-        "status": "evaluated",
-        "round": round_number,
-        "current_query": current_query,
-        "found_equivalent": found_equivalent,
-        "equivalent_query": equivalent_query if found_equivalent else None,
-        "evaluation_reason": evaluation_reason,
-        "message": f"Round {round_number} evaluation recorded. Found equivalent: {found_equivalent}." +
-                   (f" Proceed to complete_search with '{equivalent_query}'." if found_equivalent else " Continue to next round or modify query.")
-    }
-
-@function_tool
-def modify_query(original_query: str, operation_type: str, new_query: str, reason: str):
-    """
-    Modify the search query with a specific operation.
-
-    Args:
-        original_query: The original query before modification
-        operation_type: Type of modification - must be one of: "简化", "扩展", "替换", "组合"
-        new_query: The modified query after applying the operation
-        reason: Detailed explanation of why this modification was made and what insight from previous suggestions led to this change
-
-    Returns:
-        A dict containing the modification record and the new query to use for next search
-    """
-    operation_types = ["简化", "扩展", "替换", "组合"]
-    if operation_type not in operation_types:
-        return {
-            "status": "error",
-            "message": f"Invalid operation_type. Must be one of: {', '.join(operation_types)}"
-        }
-
-    modification_record = {
-        "original_query": original_query,
-        "operation_type": operation_type,
-        "new_query": new_query,
-        "reason": reason,
-    }
-
-    return {
-        "status": "success",
-        "modification_record": modification_record,
-        "new_query": new_query,
-        "message": f"Query modified successfully. Use '{new_query}' for the next search."
-    }
-
-@function_tool
-def complete_search(original_problem: str, found_query: str, source_round: int, equivalence_reason: str, total_rounds: int):
-    """
-    Mark the search as complete when an equivalent query is found.
-
-    Args:
-        original_problem: The original problem from user input
-        found_query: The equivalent query found in recommendations
-        source_round: Which round this query was found in
-        equivalence_reason: Detailed explanation of why this query is equivalent to the original problem
-        total_rounds: Total number of rounds taken
-
-    Returns:
-        A dict containing the final result
-    """
-    return {
-        "status": "completed",
-        "original_problem": original_problem,
-        "optimized_query": found_query,
-        "found_in_round": source_round,
-        "total_rounds": total_rounds,
-        "equivalence_reason": equivalence_reason,
-        "message": f"Search completed successfully! Found equivalent query '{found_query}' in round {source_round}."
-    }
-
-insrtuctions = """
-你是一个专业的搜索query优化专家,擅长通过动态探索找到最符合用户搜索习惯的query。
-
-## 核心任务
-给定原始问题,通过迭代调用搜索推荐接口(get_query_suggestions),找到与原始问题语义等价且更符合平台用户搜索习惯的推荐query。
-
-## 工作流程
-
-### 1. 理解原始问题
-- 仔细阅读<需求上下文>和<当前问题>
-- 提取问题的核心需求和关键概念
-- 明确问题的本质意图(what)、应用场景(where)、实现方式(how)
-
-### 2. 动态探索策略
-采用类似人类搜索的迭代探索方式,**每一步都必须通过函数调用记录**:
-
-**完整工具调用流程:**
-
-```
-每一轮的标准流程:
-1. get_query_suggestions(query) → 获取推荐词列表
-2. 【人工分析】逐个分析返回的推荐词,判断是否有等价query
-3. evaluate_suggestions(original_problem, current_query, round_number, found_equivalent, equivalent_query, evaluation_reason) → 记录评估结果
-4. 判断分支:
-   a) 如果found_equivalent=True → 调用 complete_search() 标记完成
-   b) 如果found_equivalent=False → 调用 modify_query() 修改query,进入下一轮
-```
-
-**第一轮(round=1):**
-```
-Step 1: get_query_suggestions(query="原始问题")
-  → 返回推荐词列表,例如:["推荐词1", "推荐词2", "推荐词3", ...]
-
-Step 2: 【分析推荐词】
-  逐个分析:
-  - "推荐词1":[分析是否等价]
-  - "推荐词2":[分析是否等价]
-  - ...
-  结论:[是否找到等价query]
-
-Step 3: evaluate_suggestions(
-    original_problem="原始问题",
-    current_query="原始问题",
-    round_number=1,
-    found_equivalent=False,  # 或True
-    equivalent_query="",  # 如果found_equivalent=True,填入找到的query
-    evaluation_reason="推荐词主要偏向[某方向],未出现与原始问题等价的表达..."
-)
-
-Step 4: 判断分支
-  - 如果found_equivalent=True → 调用 complete_search()
-  - 如果found_equivalent=False → 进入第二轮
-```
-
-**第二轮及后续(round=2,3,4,5):**
-```
-Step 1: modify_query(
-    original_query="上一轮的query",
-    operation_type="简化/扩展/替换/组合",
-    new_query="新query",
-    reason="基于第1轮推荐词观察到[具体特征],因此采用[操作]策略..."
-)
-
-Step 2: get_query_suggestions(query="新query")
-  → 返回新的推荐词列表
-
-Step 3: 【分析推荐词】
-  逐个分析并得出结论
-
-Step 4: evaluate_suggestions(
-    original_problem="原始问题",
-    current_query="新query",
-    round_number=当前轮次,
-    found_equivalent=True/False,
-    equivalent_query="找到的query或空字符串",
-    evaluation_reason="详细的评估理由..."
-)
-
-Step 5: 判断分支
-  - 如果found_equivalent=True → 调用 complete_search()
-  - 如果found_equivalent=False且未达5轮 → 继续下一轮
-  - 如果已达5轮 → 输出未找到的结论
-```
-
-**四种操作类型(operation_type):**
-- **简化**:删除冗余词汇,提取核心关键词
-  - 示例:modify_query("快速进行图片背景移除和替换", "简化", "图片背景移除", "原始query过于冗长,'快速进行'和'和替换'是修饰词,核心需求是'图片背景移除'")
-
-- **扩展**:添加场景、平台、工具类型等限定词
-  - 示例:modify_query("图片背景移除", "扩展", "在线图片背景移除工具", "从推荐词看用户更关注具体工具,添加'在线'和'工具'限定词可能更符合搜索习惯")
-
-- **替换**:使用同义词、行业术语或口语化表达
-  - 示例:modify_query("背景移除", "替换", "抠图", "推荐词中出现多个口语化表达,'抠图'是用户更常用的说法")
-
-- **组合**:调整关键词顺序或组合方式
-  - 示例:modify_query("图片背景移除", "组合", "抠图换背景", "调整表达方式,结合推荐词中高频出现的'换背景'概念")
-
-**每次修改的reason必须包含:**
-- 上一轮推荐词给你的启发(如"推荐词中多次出现'抠图'一词")
-- 为什么这样修改更符合平台用户习惯
-- 与原始问题的关系(确保核心意图不变)
-
-### 3. 等价性判断标准
-在调用 get_query_suggestions 获取推荐词后,**你需要自己分析判断**,然后通过 evaluate_suggestions 记录判断结果:
-
-**判断流程:**
-1. 获取推荐词列表后,逐个分析每个推荐词
-2. 对每个推荐词进行等价性判断(参考下面的标准)
-3. 得出结论:found_equivalent=True/False
-4. 调用 evaluate_suggestions 记录你的判断结果和理由
-
-**语义等价标准:**
-- 能够回答或解决原始问题的核心需求
-- 涵盖原始问题的关键功能或场景
-- 核心概念一致(虽然表达方式可能不同)
-
-**搜索有效性标准:**
-- 必须是平台真实推荐的query(来自 get_query_suggestions 返回)
-- 大概率能找到相关结果(基于平台用户行为数据)
-
-**可接受的差异:**
-- 表达方式不同但含义相同(如"背景移除" vs "抠图")
-- 范围略有调整但核心不变(如"图片背景移除" vs "图片抠图工具")
-- 使用同义词或口语化表达(如"快速" vs "一键")
-
-**判断后的行动:**
-- 如果found_equivalent=True → 调用 evaluate_suggestions(found_equivalent=True, equivalent_query="找到的query", ...) → 然后立即调用 complete_search() 结束
-- 如果found_equivalent=False → 调用 evaluate_suggestions(found_equivalent=False, equivalent_query="", ...) → 然后调用 modify_query() 进入下一轮
-
-### 4. 迭代终止条件
-
-**成功终止 - 调用 complete_search():**
-当在任何一轮的推荐词中找到等价query时,必须调用:
-```python
-complete_search(
-    original_problem="原始问题",
-    found_query="找到的等价query",
-    source_round=当前轮次,
-    equivalence_reason="详细说明为什么这个query与原始问题等价",
-    total_rounds=总轮次
-)
-```
-
-**失败终止 - 达到上限:**
-- 最多迭代5轮
-- 如果第5轮仍未找到,不调用 complete_search,直接输出未找到的结论
-
-**异常终止:**
-- 推荐接口返回空列表或错误
-- 函数调用失败
-
-### 5. 输出要求
-
-**当成功找到(已调用 complete_search)时:**
-```
-✓ 搜索成功完成!
-
-原始问题:[原问题]
-优化后的query:[最终找到的等价推荐query]
-找到轮次:第[N]轮
-总探索轮次:[N]轮
-
-探索路径详情:
-━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-第1轮:
-  Query: "[query1]"
-
-  调用: get_query_suggestions("[query1]")
-  返回推荐词: ["推荐词A", "推荐词B", "推荐词C", ...]
-
-  分析推荐词:
-  - "推荐词A": [不等价,原因...]
-  - "推荐词B": [不等价,原因...]
-
-  调用: evaluate_suggestions(
-    original_problem="...",
-    current_query="[query1]",
-    round_number=1,
-    found_equivalent=False,
-    equivalent_query="",
-    evaluation_reason="推荐词主要集中在[某领域],未发现与原始问题等价的表达"
-  )
-  判断: ✗ 未找到等价query
-
-第2轮:
-  调用: modify_query("[query1]", "简化", "[query2]", "[详细reason]")
-  Query: "[query2]"
-
-  调用: get_query_suggestions("[query2]")
-  返回推荐词: ["推荐词D", "推荐词E", "推荐词F", ...]
-
-  分析推荐词:
-  - "推荐词D": [不等价,原因...]
-  - "推荐词E": [不等价,原因...]
-
-  调用: evaluate_suggestions(
-    original_problem="...",
-    current_query="[query2]",
-    round_number=2,
-    found_equivalent=False,
-    equivalent_query="",
-    evaluation_reason="推荐词出现了[某特征],但仍未等价"
-  )
-  判断: ✗ 未找到等价query
-
-第3轮:
-  调用: modify_query("[query2]", "替换", "[query3]", "[详细reason]")
-  Query: "[query3]"
-
-  调用: get_query_suggestions("[query3]")
-  返回推荐词: ["推荐词G", "推荐词H(等价!)", "推荐词I", ...]
-
-  分析推荐词:
-  - "推荐词G": [不等价]
-  - "推荐词H": [等价!因为...]
-
-  调用: evaluate_suggestions(
-    original_problem="...",
-    current_query="[query3]",
-    round_number=3,
-    found_equivalent=True,
-    equivalent_query="推荐词H",
-    evaluation_reason="推荐词H与原始问题语义完全等价,因为[详细说明]"
-  )
-  判断: ✓ 找到等价query!
-
-  调用: complete_search(
-    original_problem="...",
-    found_query="推荐词H",
-    source_round=3,
-    equivalence_reason="推荐词H能够解决原始问题的核心需求...",
-    total_rounds=3
-  )
-━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-
-推荐理由:
-• 该query来自平台官方推荐,基于真实用户搜索行为
-• 语义等价分析:[具体说明为什么与原始问题等价]
-• 用户习惯匹配:[说明为什么更符合搜索习惯]
-```
-
-**未找到等价query时(未调用 complete_search):**
-```
-✗ 搜索未找到完全等价的query
-
-原始问题:[原问题]
-探索轮次:已尝试5轮(达到上限)
-
-探索路径详情:
-━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-第1轮:Query "[query1]" → 推荐词特点:[分析]
-第2轮:Query "[query2]" (简化) → 推荐词特点:[分析]
-第3轮:Query "[query3]" (替换) → 推荐词特点:[分析]
-第4轮:Query "[query4]" (扩展) → 推荐词特点:[分析]
-第5轮:Query "[query5]" (组合) → 推荐词特点:[分析]
-━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-
-探索洞察:
-• 推荐词整体偏向:[综合分析所有推荐词的共同特点]
-• 与原始问题的gap:[说明为什么一直找不到等价query]
-
-后续建议:
-1. [最可行的方案,如使用某个接近的query]
-2. [备选方案]
-3. [其他建议]
-```
-
-## 注意事项
-
-**工具调用顺序(严格遵守):**
-1. 每轮必须先调用 get_query_suggestions 获取推荐词
-2. 【你自己分析】逐个分析推荐词,判断是否有等价query
-3. 然后必须调用 evaluate_suggestions 记录你的判断结果
-4. 如果found_equivalent=True,立即调用 complete_search 结束
-5. 如果found_equivalent=False,调用 modify_query 修改query,进入下一轮
-
-**evaluate_suggestions参数要求:**
-- **found_equivalent**: 必须明确True或False,不能模糊
-- **equivalent_query**:
-  - 如果found_equivalent=True,必须填入你找到的具体推荐词(来自get_query_suggestions返回的列表)
-  - 如果found_equivalent=False,必须填空字符串 ""
-- **evaluation_reason**: 必须详细说明
-  - 如果找到:为什么该推荐词与原始问题等价
-  - 如果未找到:分析了哪些推荐词、为什么都不等价、推荐词的整体特点
-
-**其他要求:**
-- **第一轮使用原始问题**:get_query_suggestions(query="原始问题"),不做任何修改
-- **找到即complete**:一旦found_equivalent=True,必须立即调用 complete_search(),不要继续探索
-- **modify_query的reason必须详细**:必须说明基于上一轮推荐词的哪些具体特征做出此修改
-- **保持original_problem不变**:在所有evaluate_suggestions和complete_search调用中,original_problem参数必须始终是最初的原始问题
-- **round_number从1开始连续递增**:第一轮是1,第二轮是2,以此类推
-- **优先简洁口语化**:如果多个推荐词都等价,选择最简洁、最口语化的
-""".strip()
-
-agent = Agent(
-    name="Query Optimization Agent",
-    instructions=insrtuctions,
-    tools=[get_query_suggestions, evaluate_suggestions, modify_query, complete_search],
-)
-
-
-async def main():
-    set_trace()
-    user_input = read_file_as_string('input/kg_v1_single.md')
-    result = await Runner.run(agent, input=user_input)
-    print(result.final_output)
-    # The weather in Tokyo is sunny.
-
-
-if __name__ == "__main__":
-    asyncio.run(main())

+ 0 - 129
test_cache.py

@@ -1,129 +0,0 @@
-#!/usr/bin/env python3
-"""
-测试 XiaohongshuSearchRecommendations 缓存功能
-"""
-
-import sys
-import os
-import json
-import time
-
-# 添加脚本目录到路径
-sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'script', 'search_recommendations'))
-
-from xiaohongshu_search_recommendations import XiaohongshuSearchRecommendations
-
-
-def print_section(title):
-    """打印分隔符"""
-    print("\n" + "="*60)
-    print(f"  {title}")
-    print("="*60)
-
-
-def test_cache():
-    """测试缓存功能"""
-
-    # 创建客户端,设置较短的缓存时间用于测试(60秒)
-    print_section("1. 初始化客户端")
-    client = XiaohongshuSearchRecommendations(enable_cache=True, cache_ttl=60)
-    print(f"✓ 客户端初始化成功")
-    print(f"  - 缓存已启用: {client.enable_cache}")
-    print(f"  - 缓存有效期: {client.cache_ttl} 秒")
-    print(f"  - 结果目录: {client.results_base_dir}")
-
-    # 测试关键词
-    test_keyword = "川西"
-
-    # 第一次请求(应该从API获取)
-    print_section(f"2. 第一次请求关键词 '{test_keyword}'")
-    print("预期: 缓存未命中,从API获取数据")
-    start_time = time.time()
-    result1 = client.get_recommendations(test_keyword)
-    elapsed1 = time.time() - start_time
-    print(f"✓ 请求完成,耗时: {elapsed1:.2f} 秒")
-    print(f"  - 获取到 {len(result1)} 条推荐词")
-    if result1:
-        print(f"  - 示例: {result1[:3]}")
-
-    # 保存结果到文件(模拟正常使用流程)
-    if result1:
-        filepath = client.save_result(test_keyword, result1)
-        print(f"✓ 结果已保存到: {filepath}")
-
-    # 查看缓存信息
-    print_section("3. 查看缓存信息")
-    cache_info = client.get_cache_info(test_keyword)
-    print("内存缓存:")
-    print(json.dumps(cache_info["memory_cache"], ensure_ascii=False, indent=2))
-    print("\n文件缓存:")
-    print(json.dumps(cache_info["file_cache"], ensure_ascii=False, indent=2))
-
-    # 第二次请求(应该从内存缓存获取)
-    print_section(f"4. 第二次请求关键词 '{test_keyword}'(内存缓存)")
-    print("预期: 从内存缓存获取")
-    start_time = time.time()
-    result2 = client.get_recommendations(test_keyword)
-    elapsed2 = time.time() - start_time
-    print(f"✓ 请求完成,耗时: {elapsed2:.2f} 秒")
-    print(f"  - 获取到 {len(result2)} 条推荐词")
-    print(f"  - 速度提升: {(elapsed1/elapsed2):.1f}x")
-
-    # 验证结果一致性
-    if result1 == result2:
-        print("✓ 缓存数据与原始数据一致")
-    else:
-        print("✗ 警告: 缓存数据与原始数据不一致")
-
-    # 清除内存缓存,测试文件缓存
-    print_section(f"5. 清除内存缓存,测试文件缓存")
-    client.clear_memory_cache(test_keyword)
-    print("✓ 内存缓存已清除")
-
-    # 第三次请求(应该从文件缓存获取)
-    print_section(f"6. 第三次请求关键词 '{test_keyword}'(文件缓存)")
-    print("预期: 从文件缓存获取")
-    start_time = time.time()
-    result3 = client.get_recommendations(test_keyword)
-    elapsed3 = time.time() - start_time
-    print(f"✓ 请求完成,耗时: {elapsed3:.2f} 秒")
-    print(f"  - 获取到 {len(result3)} 条推荐词")
-
-    # 验证结果一致性
-    if result1 == result3:
-        print("✓ 文件缓存数据与原始数据一致")
-    else:
-        print("✗ 警告: 文件缓存数据与原始数据不一致")
-
-    # 测试禁用缓存
-    print_section(f"7. 测试禁用缓存(use_cache=False)")
-    print("预期: 直接从API获取,不使用缓存")
-    start_time = time.time()
-    result4 = client.get_recommendations(test_keyword, use_cache=False)
-    elapsed4 = time.time() - start_time
-    print(f"✓ 请求完成,耗时: {elapsed4:.2f} 秒")
-    print(f"  - 获取到 {len(result4)} 条推荐词")
-
-    # 查看最终缓存状态
-    print_section("8. 最终缓存状态")
-    cache_info = client.get_cache_info()
-    print("所有内存缓存:")
-    for keyword, info in cache_info["memory_cache"].items():
-        print(f"  - {keyword}: {info}")
-    print("\n所有文件缓存:")
-    for keyword, info in cache_info["file_cache"].items():
-        print(f"  - {keyword}: {info}")
-
-    print_section("测试完成")
-    print("✓ 所有缓存功能测试通过")
-
-
-if __name__ == "__main__":
-    try:
-        test_cache()
-    except KeyboardInterrupt:
-        print("\n\n测试被用户中断")
-    except Exception as e:
-        print(f"\n\n✗ 测试失败: {e}")
-        import traceback
-        traceback.print_exc()

File diff suppressed because it is too large
+ 183 - 68
visualization.html


+ 0 - 321
visualization/knowledge_search_traverse/convert_v8_to_graph.js

@@ -1,321 +0,0 @@
-/**
- * 将 v6.1.2.8 的 run_context.json 转换成图结构
- */
-
-function convertV8ToGraph(runContext) {
-  const nodes = {};
-  const edges = [];
-  const iterations = {};
-
-  const o = runContext.o || '原始问题';
-  const rounds = runContext.rounds || [];
-
-  // 添加原始问题根节点
-  const rootId = 'root_o';
-  nodes[rootId] = {
-    type: 'root',
-    query: o,
-    level: 0,
-    relevance_score: 1.0,
-    strategy: '原始问题',
-    iteration: 0,
-    is_terminated: false,
-    no_suggestion_rounds: 0,
-    evaluation_reason: '用户输入的原始问题',
-    is_selected: true
-  };
-
-  iterations[0] = [rootId];
-
-  // 处理每一轮
-  rounds.forEach((round, roundIndex) => {
-    if (round.type === 'initialization') {
-      // 初始化阶段:处理 seg_list
-      const roundNum = 0;
-      if (!iterations[roundNum]) iterations[roundNum] = [];
-
-      round.seg_list.forEach((seg, segIndex) => {
-        const segId = `seg_${seg.text}_${roundNum}`;
-        nodes[segId] = {
-          type: 'seg',
-          query: seg.text,
-          level: 1,
-          relevance_score: seg.score || 0,
-          strategy: '初始分词',
-          iteration: roundNum,
-          is_terminated: false,
-          evaluation_reason: '分词专家分词结果',
-          is_selected: true,
-          parent_query: o
-        };
-
-        // 添加边:root -> seg
-        edges.push({
-          from: rootId,
-          to: segId,
-          edge_type: 'root_to_seg',
-          strategy: '初始分词'
-        });
-
-        iterations[roundNum].push(segId);
-      });
-
-      // 将 seg 作为第一轮的 q
-      round.q_list_1.forEach((q, qIndex) => {
-        const qId = `q_${q.text}_r1`;
-        const segId = `seg_${q.text}_0`;
-
-        nodes[qId] = {
-          type: 'q',
-          query: q.text,
-          level: 1,
-          relevance_score: q.score || 0,
-          strategy: '来自分词',
-          iteration: 1,
-          is_terminated: false,
-          evaluation_reason: '初始Query',
-          is_selected: true,
-          parent_query: q.text,
-          from_source: 'seg'
-        };
-
-        // 添加边:seg -> q
-        if (nodes[segId]) {
-          edges.push({
-            from: segId,
-            to: qId,
-            edge_type: 'seg_to_q',
-            strategy: '作为Query'
-          });
-        }
-
-        if (!iterations[1]) iterations[1] = [];
-        iterations[1].push(qId);
-      });
-
-    } else {
-      // 普通轮次
-      const roundNum = round.round_num;
-      const nextRoundNum = roundNum + 1;
-
-      if (!iterations[nextRoundNum]) iterations[nextRoundNum] = [];
-
-      // 添加本轮的操作步骤节点
-      const stepNodes = [];
-
-      // 获取第一个输入Query作为所有操作节点的连接点(代表本轮输入)
-      // 注意:需要从已创建的节点中查找,因为节点ID可能带有index后缀
-      let firstInputQId = null;
-      if (round.input_q_list && round.input_q_list.length > 0) {
-        const firstInputQ = round.input_q_list[0];
-        // 尝试查找匹配的节点(考虑可能有index后缀)
-        firstInputQId = Object.keys(nodes).find(id => {
-          const node = nodes[id];
-          return node.type === 'q' &&
-                 node.query === firstInputQ.text &&
-                 node.iteration === roundNum;
-        });
-      }
-
-      // 步骤1:请求sug操作节点
-      if (round.sug_count > 0) {
-        const requestSugId = `operation_request_sug_r${roundNum}`;
-        nodes[requestSugId] = {
-          type: 'operation',
-          query: `步骤1: 请求建议词 (${round.sug_count}个)`,
-          level: roundNum,
-          relevance_score: 0,
-          strategy: '请求建议词',
-          iteration: roundNum,
-          operation_type: 'request_sug',
-          detail: `为${round.input_q_list?.length || 0}个Query请求建议词,获得${round.sug_count}个建议`,
-          is_selected: true
-        };
-        stepNodes.push(requestSugId);
-
-        // 平级连接:从第一个输入Query连接
-        if (firstInputQId && nodes[firstInputQId]) {
-          edges.push({
-            from: firstInputQId,
-            to: requestSugId,
-            edge_type: 'q_to_operation',
-            strategy: '请求建议词'
-          });
-        }
-      }
-
-      // 步骤2:评估sug操作节点
-      if (round.sug_count > 0) {
-        const evaluateSugId = `operation_evaluate_sug_r${roundNum}`;
-        nodes[evaluateSugId] = {
-          type: 'operation',
-          query: `步骤2: 评估建议词 (高分:${round.high_score_sug_count})`,
-          level: roundNum,
-          relevance_score: 0,
-          strategy: '评估建议词',
-          iteration: roundNum,
-          operation_type: 'evaluate_sug',
-          detail: `评估${round.sug_count}个建议词,${round.high_score_sug_count}个达到阈值`,
-          is_selected: true
-        };
-        stepNodes.push(evaluateSugId);
-
-        // 平级连接:从第一个输入Query连接
-        if (firstInputQId && nodes[firstInputQId]) {
-          edges.push({
-            from: firstInputQId,
-            to: evaluateSugId,
-            edge_type: 'q_to_operation',
-            strategy: '评估建议词'
-          });
-        }
-      }
-
-      // 步骤3:执行搜索操作节点
-      if (round.search_count > 0) {
-        const searchOpId = `operation_search_r${roundNum}`;
-        nodes[searchOpId] = {
-          type: 'operation',
-          query: `步骤3: 执行搜索 (${round.search_count}次)`,
-          level: roundNum,
-          relevance_score: 0,
-          strategy: '执行搜索',
-          iteration: roundNum,
-          operation_type: 'search',
-          search_count: round.search_count,
-          total_posts: round.total_posts,
-          detail: `搜索${round.search_count}个高分建议词,找到${round.total_posts}个帖子`,
-          is_selected: true
-        };
-        stepNodes.push(searchOpId);
-
-        // 平级连接:从第一个输入Query连接
-        if (firstInputQId && nodes[firstInputQId]) {
-          edges.push({
-            from: firstInputQId,
-            to: searchOpId,
-            edge_type: 'q_to_operation',
-            strategy: '执行搜索'
-          });
-        }
-      }
-
-      // 步骤4:加词操作节点
-      const addWordCount = round.output_q_list?.filter(q => q.from === 'add').length || 0;
-      if (addWordCount > 0) {
-        const addWordId = `operation_add_word_r${roundNum}`;
-        nodes[addWordId] = {
-          type: 'operation',
-          query: `步骤4: 智能加词 (${addWordCount}个)`,
-          level: roundNum,
-          relevance_score: 0,
-          strategy: '智能加词',
-          iteration: roundNum,
-          operation_type: 'add_word',
-          detail: `为Seed选择词并组合,生成${addWordCount}个新Query`,
-          is_selected: true
-        };
-        stepNodes.push(addWordId);
-
-        // 平级连接:从第一个输入Query连接
-        if (firstInputQId && nodes[firstInputQId]) {
-          edges.push({
-            from: firstInputQId,
-            to: addWordId,
-            edge_type: 'q_to_operation',
-            strategy: '智能加词'
-          });
-        }
-      }
-
-      // 步骤5:筛选高分sug操作节点
-      const sugCount = round.output_q_list?.filter(q => q.from === 'sug').length || 0;
-      if (sugCount > 0) {
-        const filterSugId = `operation_filter_sug_r${roundNum}`;
-        nodes[filterSugId] = {
-          type: 'operation',
-          query: `步骤5: 筛选高分sug (${sugCount}个)`,
-          level: roundNum,
-          relevance_score: 0,
-          strategy: '筛选高分sug',
-          iteration: roundNum,
-          operation_type: 'filter_sug',
-          detail: `筛选出${sugCount}个分数高于来源Query的建议词`,
-          is_selected: true
-        };
-        stepNodes.push(filterSugId);
-
-        // 平级连接:从第一个输入Query连接
-        if (firstInputQId && nodes[firstInputQId]) {
-          edges.push({
-            from: firstInputQId,
-            to: filterSugId,
-            edge_type: 'q_to_operation',
-            strategy: '筛选高分sug'
-          });
-        }
-      }
-
-      // 将操作节点添加到当前轮次
-      stepNodes.forEach(nodeId => {
-        if (!iterations[roundNum]) iterations[roundNum] = [];
-        iterations[roundNum].push(nodeId);
-      });
-
-      // 处理输出的 q_list
-      if (round.output_q_list) {
-        round.output_q_list.forEach((q, qIndex) => {
-          const qId = `q_${q.text}_r${nextRoundNum}_${qIndex}`;
-
-          nodes[qId] = {
-            type: 'q',
-            query: q.text,
-            level: nextRoundNum,
-            relevance_score: q.score || 0,
-            strategy: q.from === 'add' ? '加词生成' : q.from === 'sug' ? '建议词' : '未知',
-            iteration: nextRoundNum,
-            is_terminated: false,
-            evaluation_reason: `来源: ${q.from}`,
-            is_selected: true,
-            from_source: q.from
-          };
-
-          // 连接到对应的操作节点
-          if (q.from === 'add') {
-            // 加词生成的Query连接到加词操作节点
-            const addWordId = `operation_add_word_r${roundNum}`;
-            if (nodes[addWordId]) {
-              edges.push({
-                from: addWordId,
-                to: qId,
-                edge_type: 'operation_to_q',
-                strategy: '加词生成'
-              });
-            }
-          } else if (q.from === 'sug') {
-            // sug生成的Query连接到筛选sug操作节点
-            const filterSugId = `operation_filter_sug_r${roundNum}`;
-            if (nodes[filterSugId]) {
-              edges.push({
-                from: filterSugId,
-                to: qId,
-                edge_type: 'operation_to_q',
-                strategy: '建议词生成'
-              });
-            }
-          }
-
-          iterations[nextRoundNum].push(qId);
-        });
-      }
-    }
-  });
-
-  return {
-    nodes,
-    edges,
-    iterations
-  };
-}
-
-module.exports = { convertV8ToGraph };

+ 18 - 6
visualization/knowledge_search_traverse/convert_v8_to_graph_v3.js

@@ -57,15 +57,15 @@ function convertV8ToGraphV2(runContext, searchResults, extractionData) {
       if (!iterations[roundNum * 10]) iterations[roundNum * 10] = [];
       iterations[roundNum * 10].push(roundId);
 
-      // 步骤1: 分段及拆词
+      // 步骤1: 分段
       if (round.segments && round.segments.length > 0) {
         const segStepId = `step_seg_r${roundNum}`;
         nodes[segStepId] = {
           type: 'step',
-          query: `步骤1: 分段及拆词 (${round.segments.length}个segment)`,
+          query: `步骤1: 分段 (${round.segments.length}个segment)`,
           level: roundNum * 10 + 1,
           relevance_score: 0,
-          strategy: '分段及拆词',
+          strategy: '分段',
           iteration: roundNum,
           is_selected: true
         };
@@ -74,7 +74,7 @@ function convertV8ToGraphV2(runContext, searchResults, extractionData) {
           from: roundId,
           to: segStepId,
           edge_type: 'round_to_step',
-          strategy: '分段及拆词'
+          strategy: '分段'
         });
 
         iterations[roundNum * 10 + 1] = [segStepId];
@@ -458,7 +458,13 @@ function convertV8ToGraphV2(runContext, searchResults, extractionData) {
                   image_list: imageList,
                   interact_info: post.interact_info || {},
                   // 附加多模态提取数据
-                  extraction: extractionData && extractionData[post.note_id] ? extractionData[post.note_id] : null
+                  extraction: extractionData && extractionData[post.note_id] ? extractionData[post.note_id] : null,
+                  // 评估数据
+                  is_knowledge: post.is_knowledge !== undefined ? post.is_knowledge : null,
+                  knowledge_reason: post.knowledge_reason || '',
+                  post_relevance_score: post.relevance_score !== undefined ? post.relevance_score : null,
+                  relevance_level: post.relevance_level || '',
+                  relevance_reason: post.relevance_reason || ''
                 };
 
                 edges.push({
@@ -969,7 +975,13 @@ function convertV8ToGraphSimplified(runContext, searchResults, extractionData) {
       foundByQueries: Array.from(post.foundByQueries),
       foundInRounds: Array.from(post.foundInRounds),
       // 附加多模态提取数据
-      extraction: extractionData && extractionData[post.note_id] ? extractionData[post.note_id] : null
+      extraction: extractionData && extractionData[post.note_id] ? extractionData[post.note_id] : null,
+      // 评估数据
+      is_knowledge: post.is_knowledge !== undefined ? post.is_knowledge : null,
+      knowledge_reason: post.knowledge_reason || '',
+      post_relevance_score: post.relevance_score !== undefined ? post.relevance_score : null,
+      relevance_level: post.relevance_level || '',
+      relevance_reason: post.relevance_reason || ''
     };
 
     if (!iterations[100]) iterations[100] = [];

+ 28857 - 0
visualization/knowledge_search_traverse/debug_component.jsx

@@ -0,0 +1,28857 @@
+
+import React, { useState, useCallback, useMemo, useEffect } from 'react';
+import { createRoot } from 'react-dom/client';
+import {
+  ReactFlow,
+  Controls,
+  Background,
+  useNodesState,
+  useEdgesState,
+  Handle,
+  Position,
+  useReactFlow,
+  ReactFlowProvider,
+} from '@xyflow/react';
+import '@xyflow/react/dist/style.css';
+
+const data = {
+  "nodes": {
+    "root_o": {
+      "type": "root",
+      "query": "如何制作反映人类双标行为的猫咪表情包梗图",
+      "level": 0,
+      "relevance_score": 1,
+      "strategy": "原始问题",
+      "iteration": 0,
+      "is_selected": true
+    },
+    "round_0": {
+      "type": "round",
+      "query": "Round 0 (初始化)",
+      "level": 0,
+      "relevance_score": 0,
+      "strategy": "初始化",
+      "iteration": 0,
+      "is_selected": true
+    },
+    "step_seg_r0": {
+      "type": "step",
+      "query": "步骤1: 分段 (4个segment)",
+      "level": 1,
+      "relevance_score": 0,
+      "strategy": "分段",
+      "iteration": 0,
+      "is_selected": true
+    },
+    "segment_0_r0": {
+      "type": "segment",
+      "query": "[疑问引导] 如何",
+      "level": 2,
+      "relevance_score": 0.024,
+      "evaluationReason": "【评估对象】词条\"如何\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】词条“如何”本身不包含任何动作意图,无法与原始问题的核心动机“制作”进行匹配。\n【品类维度 0.08】原始问题是关于制作特定主题(人类双标行为的猫咪表情包梗图)的方法,而词条“如何”是一个高度通用的疑问词,不包含任何具体品类信息。根据评估原则,通用概念不等于特定概念,因此品类匹配度极低。\n【最终得分 0.02】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.02已≤0.5",
+      "strategy": "疑问引导",
+      "iteration": 0,
+      "is_selected": true,
+      "segment_type": "疑问引导",
+      "domain_index": 0,
+      "domain_type": "疑问引导"
+    },
+    "word_如何_seg0_0": {
+      "type": "word",
+      "query": "如何",
+      "level": 3,
+      "relevance_score": 0.024,
+      "evaluationReason": "【评估对象】词条\"如何\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】词条“如何”本身不包含任何动作意图,无法与原始问题的核心动机“制作”进行匹配。\n【品类维度 0.08】原始问题是关于制作特定主题(人类双标行为的猫咪表情包梗图)的方法,而词条“如何”是一个高度通用的疑问词,不包含任何具体品类信息。根据评估原则,通用概念不等于特定概念,因此品类匹配度极低。\n【最终得分 0.02】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.02已≤0.5",
+      "strategy": "Word",
+      "iteration": 0,
+      "is_selected": true
+    },
+    "segment_1_r0": {
+      "type": "segment",
+      "query": "[核心动作] 制作",
+      "level": 2,
+      "relevance_score": 0.71,
+      "evaluationReason": "【评估对象】词条\"制作\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.98】词条“制作”与原始问题中的核心动作“制作”完全一致,是原始问题动作的精确匹配。\n【品类维度 0.08】原始问题是关于制作特定主题(人类双标行为的猫咪表情包梗图)的方法,而词条“制作”是一个非常通用的动词,没有包含任何品类信息,属于过度泛化。通用概念不等于特定概念,因此品类匹配度极低。\n【最终得分 0.71】\n【规则说明】规则A:动机高分保护生效(动机0.98≥0.8),实际得分0.71已≥0.7",
+      "strategy": "核心动作",
+      "iteration": 0,
+      "is_selected": true,
+      "segment_type": "核心动作",
+      "domain_index": 1,
+      "domain_type": "核心动作"
+    },
+    "word_制作_seg1_0": {
+      "type": "word",
+      "query": "制作",
+      "level": 3,
+      "relevance_score": 0.71,
+      "evaluationReason": "【评估对象】词条\"制作\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.98】词条“制作”与原始问题中的核心动作“制作”完全一致,是原始问题动作的精确匹配。\n【品类维度 0.08】原始问题是关于制作特定主题(人类双标行为的猫咪表情包梗图)的方法,而词条“制作”是一个非常通用的动词,没有包含任何品类信息,属于过度泛化。通用概念不等于特定概念,因此品类匹配度极低。\n【最终得分 0.71】\n【规则说明】规则A:动机高分保护生效(动机0.98≥0.8),实际得分0.71已≥0.7",
+      "strategy": "Word",
+      "iteration": 0,
+      "is_selected": true
+    },
+    "segment_2_r0": {
+      "type": "segment",
+      "query": "[修饰短语] 反映人类双标行为的",
+      "level": 2,
+      "relevance_score": 0.09,
+      "evaluationReason": "【评估对象】词条\"反映人类双标行为的\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】词条“反映人类双标行为的”是一个描述性短语,没有明确的动作意图,无法与原始问题的核心动机“制作”进行匹配。\n【品类维度 0.30】词条'反映人类双标行为的'是原始问题中的一个限定词,但缺少核心主体'猫咪表情包梗图',因此匹配度较低。\n【最终得分 0.09】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.09已≤0.5",
+      "strategy": "修饰短语",
+      "iteration": 0,
+      "is_selected": true,
+      "segment_type": "修饰短语",
+      "domain_index": 2,
+      "domain_type": "修饰短语"
+    },
+    "word_反映_seg2_0": {
+      "type": "word",
+      "query": "反映",
+      "level": 3,
+      "relevance_score": 0.024,
+      "evaluationReason": "【评估对象】词条\"反映\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题的核心动机是“制作”,而词条“反映”虽然是原始问题中的一个动词,但它不是核心动机,且词条本身无法构成一个完整的动作意图,因此动机维度得分为0。\n【品类维度 0.08】原始问题是关于制作特定主题(人类双标行为的猫咪表情包梗图)的品类,而词条“反映”是一个通用动词,不包含任何品类信息,属于过度泛化,因此品类匹配度极低。\n【最终得分 0.02】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.02已≤0.5",
+      "strategy": "Word",
+      "iteration": 0,
+      "is_selected": true
+    },
+    "word_人类_seg2_1": {
+      "type": "word",
+      "query": "人类",
+      "level": 3,
+      "relevance_score": 0.024,
+      "evaluationReason": "【评估对象】词条\"人类\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】词条“人类”是一个名词,没有明确的动作意图,无法与原始问题的核心动机“制作”进行匹配。\n【品类维度 0.08】原始问题是关于制作特定主题(人类双标行为)的猫咪表情包梗图,而词条“人类”是一个高度泛化的概念,虽然原始问题中包含“人类”一词,但词条本身没有提供任何关于“双标行为”、“猫咪表情包”或“梗图制作”的特定信息,属于通用概念与特定概念的匹配,因此得分较低。\n【最终得分 0.02】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.02已≤0.5",
+      "strategy": "Word",
+      "iteration": 0,
+      "is_selected": true
+    },
+    "word_双标_seg2_2": {
+      "type": "word",
+      "query": "双标",
+      "level": 3,
+      "relevance_score": 0.024,
+      "evaluationReason": "【评估对象】词条\"双标\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】词条“双标”是一个名词,没有明确的动作意图,因此无法与原始问题的核心动机“制作”进行匹配。\n【品类维度 0.08】原始问题是关于制作特定主题(人类双标行为)的特定形式(猫咪表情包梗图),而词条“双标”是一个通用概念,虽然是原始问题中的一个限定词,但词条本身没有包含原始问题的主体和限定词,属于过度泛化。\n【最终得分 0.02】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.02已≤0.5",
+      "strategy": "Word",
+      "iteration": 0,
+      "is_selected": true
+    },
+    "word_行为_seg2_3": {
+      "type": "word",
+      "query": "行为",
+      "level": 3,
+      "relevance_score": 0.024,
+      "evaluationReason": "【评估对象】词条\"行为\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】词条“行为”是一个名词,没有明确的动作意图,无法与原始问题的核心动机“制作”进行匹配。\n【品类维度 0.08】原始问题是关于制作特定主题(人类双标行为)的特定形式(猫咪表情包梗图)的指南,而词条“行为”是一个非常通用的概念,没有限定词,无法与原始问题的特定品类匹配,属于过度泛化。\n【最终得分 0.02】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.02已≤0.5",
+      "strategy": "Word",
+      "iteration": 0,
+      "is_selected": true
+    },
+    "segment_3_r0": {
+      "type": "segment",
+      "query": "[中心名词] 猫咪表情包梗图",
+      "level": 2,
+      "relevance_score": 0.23399999999999999,
+      "evaluationReason": "【评估对象】词条\"猫咪表情包梗图\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】词条“猫咪表情包梗图”是一个名词短语,没有明确的动作意图,因此无法与原始问题的“制作”动作进行匹配。\n【品类维度 0.78】词条'猫咪表情包梗图'与原始问题中的核心主体'猫咪表情包梗图'完全匹配,但缺少了限定词'反映人类双标行为的',因此给予较高正向分数。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5",
+      "strategy": "中心名词",
+      "iteration": 0,
+      "is_selected": true,
+      "segment_type": "中心名词",
+      "domain_index": 3,
+      "domain_type": "中心名词"
+    },
+    "word_猫咪_seg3_0": {
+      "type": "word",
+      "query": "猫咪",
+      "level": 3,
+      "relevance_score": 0.09,
+      "evaluationReason": "【评估对象】词条\"猫咪\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】词条“猫咪”是一个名词,没有明确的动作意图,无法与原始问题的“制作”动作进行匹配。\n【品类维度 0.30】原始问题的主体是'猫咪表情包梗图',词条是'猫咪'。词条包含了原始问题核心主体的一部分,但缺少了'表情包梗图'这个关键限定词,且原始问题中的'猫咪'是作为表情包的主体,而词条'猫咪'是泛指,语义身份存在差异。\n【最终得分 0.09】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.09已≤0.5",
+      "strategy": "Word",
+      "iteration": 0,
+      "is_selected": true
+    },
+    "word_表情包_seg3_1": {
+      "type": "word",
+      "query": "表情包",
+      "level": 3,
+      "relevance_score": 0.15,
+      "evaluationReason": "【评估对象】词条\"表情包\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题的核心动机是“制作”,而词条“表情包”是一个名词,没有明确的动作意图,因此无法评估动作匹配度。\n【品类维度 0.50】词条“表情包”是原始问题“猫咪表情包梗图”的核心主体,但缺少了“猫咪”、“梗图”等限定词,属于核心主体匹配但限定词缺失。\n【最终得分 0.15】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.15已≤0.5",
+      "strategy": "Word",
+      "iteration": 0,
+      "is_selected": true
+    },
+    "word_梗图_seg3_2": {
+      "type": "word",
+      "query": "梗图",
+      "level": 3,
+      "relevance_score": 0.024,
+      "evaluationReason": "【评估对象】词条\"梗图\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】词条“梗图”是一个名词,没有明确的动作意图,因此无法与原始问题的核心动机“制作”进行匹配。\n【品类维度 0.08】原始问题是关于制作特定主题(人类双标行为)和特定形式(猫咪表情包梗图)的梗图,而词条“梗图”是一个非常通用的概念,没有包含原始问题中的任何限定词(人类双标行为、猫咪表情包)。通用概念不等于特定概念,因此品类匹配度低。\n【最终得分 0.02】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.02已≤0.5",
+      "strategy": "Word",
+      "iteration": 0,
+      "is_selected": true
+    },
+    "round_1": {
+      "type": "round",
+      "query": "Round 1",
+      "level": 10,
+      "relevance_score": 0,
+      "strategy": "第1轮",
+      "iteration": 1,
+      "is_selected": true
+    },
+    "step_sug_r1": {
+      "type": "step",
+      "query": "步骤1: 请求&评估推荐词 (90个)",
+      "level": 11,
+      "relevance_score": 0,
+      "strategy": "请求&评估推荐词",
+      "iteration": 1,
+      "is_selected": true
+    },
+    "q_如何_r1_0": {
+      "type": "q",
+      "query": "[Q] 如何",
+      "level": 12,
+      "relevance_score": 0.024,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": 0,
+      "domain_type": "疑问引导"
+    },
+    "sug_如何快速减肥_r1_q0_0": {
+      "type": "sug",
+      "query": "[SUG] 如何快速减肥",
+      "level": 13,
+      "relevance_score": -0.53,
+      "evaluationReason": "【评估对象】词条\"如何快速减肥\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包\n【动机维度 -0.50】原始问题动机为“制作”,sug词条动机为“减肥”。两者动作意图完全不相关,且制作与减肥的方向也存在明显的冲突。\n【品类维度 -0.55】原始问题核心是“人类双标行为的猫咪表情包梗图”,涉及到“猫咪”、“表情包”、“梗图”,而sug词条是“如何快速减肥”,二者品类完全不相关,对象完全错位。\n【延伸词维度 -0.60】sug词条「快速减肥」与原始问题「制作反映人类双标行为的猫咪表情包梗图」在主题、对象、目的上完全不相关,引入了与原始问题核心需求无关的全新主题,严重稀释了原始问题的聚焦度,属于作用域稀释型延伸词。\n【最终得分 -0.53】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_如何培养男人主动给你花钱_r1_q0_1": {
+      "type": "sug",
+      "query": "[SUG] 如何培养男人主动给你花钱",
+      "level": 13,
+      "relevance_score": -0.8,
+      "evaluationReason": "【评估对象】词条\"如何培养男人主动给你花钱\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 -0.80】原始问题的核心动机是「制作」反映特定主题的表情包梗图。sug词条的核心动机是「培养」。两者在动作意图上完全不相关,且方向大相径庭,一个侧重创作,一个侧重影响或改变他人的行为。\n【品类维度 -0.85】原始问题对象层为「猫咪表情包梗图」,场景层为「人类双标行为」。Sug词条对象层为「男人」的「花钱行为」,两者话题品类完全不同,对象层与场景层均不匹配,存在负向偏离。\n【延伸词维度 -0.60】sug词条与原始问题完全不相关,原始问题是关于制作猫咪表情包梗图,而sug词条是关于培养男人花钱,两者在主题、目的和内容上都毫无关联,属于作用域稀释型,且稀释程度极高。\n【最终得分 -0.80】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_如何快速挣到钱_r1_q0_2": {
+      "type": "sug",
+      "query": "[SUG] 如何快速挣到钱",
+      "level": 13,
+      "relevance_score": -0.4,
+      "evaluationReason": "【评估对象】词条\"如何快速挣到钱\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.00】原始问题是「制作表情包梗图」,sug词条是「挣钱」。两者在动机维度完全不匹配。\n【品类维度 -0.85】原始问题涉及“猫咪表情包梗图”和“人类双标行为”的创作主题,sug词条“如何快速挣到钱”的主体是金钱获取方式。两者内容主体完全无关,且品类冲突。\n【延伸词维度 -0.60】sug词条「如何快速挣到钱」与原始问题「如何制作反映人类双标行为的猫咪表情包梗图」在主题、目的和内容上完全不相关,引入了与原始问题核心需求无关的全新主题,严重稀释了原始问题的聚焦度,属于作用域稀释型延伸词。\n【最终得分 -0.40】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_如何和女生聊天找话题_r1_q0_3": {
+      "type": "sug",
+      "query": "[SUG] 如何和女生聊天找话题",
+      "level": 13,
+      "relevance_score": -0.38000000000000006,
+      "evaluationReason": "【评估对象】词条\"如何和女生聊天找话题\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包/梗图\n【动机维度 0.00】原始问题的核心动机是学习「制作」表情包/梗图,而sug词条的动机是学习「聊天/找话题」。两者动机完全不匹配。\n【品类维度 -0.80】原始问题内容主体为《人类双标行为的猫咪表情包梗图》,sug词条内容主体为《和女生聊天找话题》。两者核心对象和场景完全不匹配,是完全不同领域的类别。\n【延伸词维度 -0.60】sug词条「如何和女生聊天找话题」与原始问题「如何制作反映人类双标行为的猫咪表情包梗图」在主题、目的和对象上完全不相关,属于作用域稀释型,且偏离度极高。\n【最终得分 -0.38】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_如何快速入睡_r1_q0_4": {
+      "type": "sug",
+      "query": "[SUG] 如何快速入睡",
+      "level": 13,
+      "relevance_score": -0.78,
+      "evaluationReason": "【评估对象】词条\"如何快速入睡\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图,表达特定主题\n【动机维度 -0.80】原始问题的核心动机是「制作」具有特定意义的「表情包梗图」,而sug词条的动机是「入睡」。两者动作意图完全不相关,且方向相反,一个是主动创造,另一个是被动状态。\n【品类维度 -0.80】原始问题内容主体为《人类双标行为的猫咪表情包梗图制作》,sug词条内容主体为《入睡》的方法。两者核心对象和场景完全不匹配,品类差异巨大,语义错位。\n【延伸词维度 -0.60】sug词条「如何快速入睡」与原始问题「如何制作反映人类双标行为的猫咪表情包梗图」在主题、目的和内容上完全不相关,属于作用域稀释型,且偏离度极高。\n【最终得分 -0.78】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_如何让男生持续上头_r1_q0_5": {
+      "type": "sug",
+      "query": "[SUG] 如何让男生持续上头",
+      "level": 13,
+      "relevance_score": -0.4,
+      "evaluationReason": "【评估对象】词条\"如何让男生持续上头\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包、梗图)\n【动机维度 0.00】原始问题的核心动机是「制作」表情包/梗图,而sug词条的动机是「让」男生如何,两者动作意图完全不相关,方向不同,因此得分为0。\n【品类维度 -0.85】原始问题对象层为「猫咪表情包梗图」,场景层为「人类双标行为」。Sug词条对象层为「男生」,缺失全部核心对象和场景,类别完全冲突,为负向偏离。\n【延伸词维度 -0.60】sug词条与原始问题完全无关,原始问题是关于制作表情包梗图,而sug词条是关于两性情感,两者在主题、目的和内容上均无任何关联,属于作用域稀释型,且稀释程度非常高。\n【最终得分 -0.40】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_如何逼自己自律学习_r1_q0_6": {
+      "type": "sug",
+      "query": "[SUG] 如何逼自己自律学习",
+      "level": 13,
+      "relevance_score": -0.4,
+      "evaluationReason": "【评估对象】词条\"如何逼自己自律学习\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包\n【动机维度 0.00】原始问题的核心动机是「制作」表情包,而sug词条的核心动机是「逼」自己学习。两者动作意图完全不匹配。\n【品类维度 -0.85】原始问题主题集中在“猫咪表情包梗图”这一创作内容,sug词条“如何逼自己自律学习”与原始问题的主体内容(猫、表情包、梗图)完全不符,属于完全不同的品类,且没有可关联的限定词。\n【延伸词维度 -0.60】sug词条「如何逼自己自律学习」与原始问题「如何制作反映人类双标行为的猫咪表情包梗图」在主题、目的和对象上完全不相关,引入了与原始问题核心需求相悖的全新主题,严重稀释了原始问题的聚焦度,导致负面影响。\n【最终得分 -0.40】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_如何连接别人家的加密wifi_r1_q0_7": {
+      "type": "sug",
+      "query": "[SUG] 如何连接别人家的加密wifi",
+      "level": 13,
+      "relevance_score": -0.4,
+      "evaluationReason": "【评估对象】词条\"如何连接别人家的加密wifi\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题意图是「制作」反映人类双标行为的猫咪表情包梗图,sug词条是「连接」别人家的加密wifi,两者动作意图完全不相关。\n【品类维度 -0.85】原始问题涉及“猫咪表情包梗图”这一创作类内容主体,且有具体行为“双标”限定。sug词条为“如何连接别人家的加密wifi”这一计算机网络类内容,二者品类完全不符,且限定词也无关联,属于完全负向的偏离。\n【延伸词维度 -0.60】sug词条「如何连接别人家的加密wifi」与原始问题「如何制作反映人类双标行为的猫咪表情包梗图」在主题、目的和内容上完全不相关,引入了与原始问题核心需求无关的全新主题,严重稀释了原始问题的聚焦度,导致负面影响。\n【最终得分 -0.40】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_如何养好头发_r1_q0_8": {
+      "type": "sug",
+      "query": "[SUG] 如何养好头发",
+      "level": 13,
+      "relevance_score": -0.8,
+      "evaluationReason": "【评估对象】词条\"如何养好头发\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 -0.80】原始问题的核心动机是「制作」反映特定主题的表情包梗图。sug词条的动机是「养好」头发。两者之间没有任何关联性和相似性,属于完全不相关的动作意图。\n【品类维度 -0.85】原始问题对象层为「人类双标行为的猫咪表情包梗图」,限定词为「反映/制作」,场景层无。 Sug词条对象层为「头发」,限定词为「养好」。两者在对象层和限定词上完全不匹配,品类冲突,属于负向偏离。\n【延伸词维度 -0.60】sug词条「如何养好头发」与原始问题「如何制作反映人类双标行为的猫咪表情包梗图」完全不相关,引入了与原始问题核心目的和作用域完全无关的全新主题,严重稀释了原始问题的聚焦度,导致内容偏离,属于作用域稀释型。\n【最终得分 -0.80】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_如何治疗早泻时间短_r1_q0_9": {
+      "type": "sug",
+      "query": "[SUG] 如何治疗早泻时间短",
+      "level": 13,
+      "relevance_score": -0.46,
+      "evaluationReason": "【评估对象】词条\"如何治疗早泻时间短\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作/制作梗图\n【动机维度 0.00】原始问题的核心动机是「制作」,sug词条的核心动机是「治疗」。两者动作意图完全无关。\n【品类维度 -1.00】原始问题涉及「猫咪表情包梗图」这一创意内容制作领域,sug词条「如何治疗早泻时间短」属于医疗健康领域,两者在内容主体上完全不相关,且领域冲突,评分为最低。\n【延伸词维度 -0.60】sug词条「如何治疗早泻时间短」与原始问题「如何制作反映人类双标行为的猫咪表情包梗图」在主题、目的、对象和场景上完全不相关,引入了与原始问题完全无关的医学健康领域内容,严重稀释了原始问题的聚焦度,导致内容偏离,属于作用域稀释型延伸词。\n【最终得分 -0.46】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "q_制作_r1_1": {
+      "type": "q",
+      "query": "[Q] 制作",
+      "level": 12,
+      "relevance_score": 0.71,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": 1,
+      "domain_type": "核心动作"
+    },
+    "sug_制作ppt_r1_q1_0": {
+      "type": "sug",
+      "query": "[SUG] 制作ppt",
+      "level": 13,
+      "relevance_score": -0.1600000000000001,
+      "evaluationReason": "【评估对象】词条\"制作ppt\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】原始问题的核心动机是「制作」反映人类双标行为的猫咪表情包梗图。\n【动机维度 0.35】原始问题的动机是「制作」表情包梗图,sug词条的动机也是「制作」ppt。两者核心动作均为「制作」,属于同一大类,动作方向有间接关联,但制作的对象(表情包梗图 vs ppt)完全不同,因此属于弱相关。\n【品类维度 -0.80】原始问题核心是「猫咪表情包梗图」,涉及「人类双标行为」这一限定。而sug词条「制作ppt」内容主体完全偏离,与原始问题无任何关联,属于品类错位。\n【延伸词维度 -0.15】sug词条「制作ppt」与原始问题「制作反映人类双标行为的猫咪表情包梗图」的核心目的和对象完全不符,引入了无关的工具和主题,严重稀释了原始问题的聚焦度,属于作用域稀释型。\n【最终得分 -0.16】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.71
+    },
+    "sug_制作表情包_r1_q1_1": {
+      "type": "sug",
+      "query": "[SUG] 制作表情包",
+      "level": 13,
+      "relevance_score": 0.815,
+      "evaluationReason": "【评估对象】词条\"制作表情包\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.95】原始问题的核心动机是「制作」,sug词条「制作表情包」的核心动机也是「制作」。sug词条的动作意图和原始问题完全一致。\n【品类维度 0.50】原始问题对象层为“猫咪表情包梗图”,限定词有“反映人类双标行为”;sug词对象层为“表情包”,是原始问题的泛化对象层,但缺少所有的限定词,故给予中等匹配分。\n【延伸词维度 0.00】sug词条未引入延伸词,所有词汇均属于原始问题作用域范围。sug词条是原始问题核心动作的概括,不构成延伸。\n【最终得分 0.81】\n【规则说明】情况4:无延伸词,权重调整为 动机70% + 品类30%",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.71
+    },
+    "sug_制作冰糖葫芦_r1_q1_2": {
+      "type": "sug",
+      "query": "[SUG] 制作冰糖葫芦",
+      "level": 13,
+      "relevance_score": -0.8,
+      "evaluationReason": "【评估对象】词条\"制作冰糖葫芦\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 -0.80】原始问题核心动机是“制作”,sug词条「制作冰糖葫芦」中也包含“制作”的动作。但制作表情包梗图和制作冰糖葫芦是完全不同的制作方向和目的,动机上完全不相关。\n【品类维度 -0.85】原始问题涉及「猫咪表情包梗图」、「双标行为」内容,而sug词条是「制作冰糖葫芦」,两者在对象层和场景层均完全不匹配,是完全不同品类的内容,因此评为负分。\n【延伸词维度 -0.60】sug词条「制作冰糖葫芦」与原始问题「制作反映人类双标行为的猫咪表情包梗图」在主题和目的上完全不相关,引入了与原始问题核心需求无关的全新概念,严重稀释了原始问题的聚焦度,属于作用域稀释型,且程度较深。\n【最终得分 -0.80】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.71
+    },
+    "sug_制作ppt的ai软件_r1_q1_3": {
+      "type": "sug",
+      "query": "[SUG] 制作ppt的ai软件",
+      "level": 13,
+      "relevance_score": -0.38000000000000006,
+      "evaluationReason": "【评估对象】词条\"制作ppt的ai软件\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题的核心动机是“制作”,sug词条「制作ppt的ai软件」的核心动机也是“制作”。尽管两者都涉及“制作”,但制作的对象完全不同,sug词条无法帮助原始问题的「制作表情包」行为,因此动机匹配度为0。\n【品类维度 -0.80】原始问题问的是「人类双标行为」「猫咪表情包梗图」的「制作」方法,sug词条是「制作ppt的ai软件」,二者在对象层和场景层完全不匹配,品类冲突。\n【延伸词维度 -0.60】sug词条「制作ppt的ai软件」中的所有词汇,如「制作」、「ppt」、「ai」、「软件」,均与原始问题「如何制作反映人类双标行为的猫咪表情包梗图」的作用域(制作、猫咪表情包梗图、人类双标行为)完全不符,引入了全新的、不相关的概念,严重稀释了原始问题的聚焦度,导致内容完全偏离,属于作用域稀释型延伸词。\n【最终得分 -0.38】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.71
+    },
+    "sug_制作视频_r1_q1_4": {
+      "type": "sug",
+      "query": "[SUG] 制作视频",
+      "level": 13,
+      "relevance_score": -0.09500000000000001,
+      "evaluationReason": "【评估对象】词条\"制作视频\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题核心动机是“制作”,平台sug词条核心动机也是“制作”,但在对象上存在差异。原始问题是制作「表情包梗图」,sug词条是制作「视频」。虽然都是『制作』,但『表情包』和『视频』是两个完全不同的事物,无法形成直接的上下属或并列关系,故无法形成高相关的动机匹配,但又不能完全否定。\n【品类维度 -0.20】原始问题是关于“猫咪表情包梗图”,sug词条是“制作视频”,核心对象类型完全不匹配,一个需要图片素材,一个需要视频素材,存在品类错位。\n【延伸词维度 -0.15】原始问题是制作「表情包梗图」,而sug词条是「制作视频」。视频与表情包梗图是两种不同的内容形式,引入「视频」稀释了原始问题对「表情包梗图」的聚焦度,降低了内容的针对性,属于作用域稀释型。\n【最终得分 -0.10】",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.71
+    },
+    "sug_制作美食_r1_q1_5": {
+      "type": "sug",
+      "query": "[SUG] 制作美食",
+      "level": 13,
+      "relevance_score": -0.6800000000000002,
+      "evaluationReason": "【评估对象】词条\"制作美食\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 -0.60】原始问题的核心动机是“制作”(表情包梗图),sug词条的核心动机是“制作”(美食),两者动作相同但制作的对象和场景完全不同,动机意图方向明显偏离。\n【品类维度 -0.80】原始问题核心对象为「猫咪表情包梗图」,sug词条核心对象为「美食」。两者对象完全不相关,品类冲突,属于负向偏离。\n【延伸词维度 -0.60】sug词条「制作美食」与原始问题「制作反映人类双标行为的猫咪表情包梗图」在动机、对象、场景上均不相关,属于完全无关的延伸,严重稀释了原始问题的核心意图。\n【最终得分 -0.68】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.71
+    },
+    "sug_制作简历_r1_q1_6": {
+      "type": "sug",
+      "query": "[SUG] 制作简历",
+      "level": 13,
+      "relevance_score": -0.6800000000000002,
+      "evaluationReason": "【评估对象】词条\"制作简历\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 -0.60】原始问题核心动机是「制作」反映人类双标行为的猫咪表情包梗图,sug词条核心动机是「制作」简历。虽然两者都有「制作」动作,但sug词条提供的「制作简历」和原始问题「制作梗图」在具体行为和目的上完全不相关。\n【品类维度 -0.80】原始问题对象层为「猫咪表情包梗图」,限定词为「人类双标行为」。sug词条对象层为「简历」,二者对象层完全不匹配,品类冲突严重,功能等方面属于完全不同领域。\n【延伸词维度 -0.60】sug词条「制作简历」与原始问题「制作反映人类双标行为的猫咪表情包梗图」在主题、对象和目的上完全不相关,引入了与原始问题核心需求无关的全新主题,严重稀释了原始问题的聚焦度,属于作用域稀释型,且程度较深。\n【最终得分 -0.68】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.71
+    },
+    "sug_制作饮品_r1_q1_7": {
+      "type": "sug",
+      "query": "[SUG] 制作饮品",
+      "level": 13,
+      "relevance_score": -0.6600000000000001,
+      "evaluationReason": "【评估对象】词条\"制作饮品\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 -0.80】原始问题的核心动机是“制作”(表情包梗图),sug词条的动机是“制作”(饮品),两者均有制作行为,但动作对象完全不同,导致制作行为偏离很大。\n【品类维度 -0.50】原始问题核心是《表现人类双标行为的猫咪表情包梗图》,sug词是《饮品》,对象层完全不同,且无任何场景限定词匹配。客体类别完全偏离,负向抵消了动作的关联性。\n【延伸词维度 -0.60】sug词条「制作饮品」与原始问题「制作反映人类双标行为的猫咪表情包梗图」在对象和目的上完全不相关,引入了与原始问题核心需求相悖的全新主题,严重稀释了原始问题的聚焦度,属于作用域稀释型。\n【最终得分 -0.66】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.71
+    },
+    "sug_制作小房子_r1_q1_8": {
+      "type": "sug",
+      "query": "[SUG] 制作小房子",
+      "level": 13,
+      "relevance_score": -0.20500000000000007,
+      "evaluationReason": "【评估对象】词条\"制作小房子\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.35】原始问题核心动机是“制作”,sug词条核心动机也是“制作”,两者动作意图相同,但制作的对象完全不同,属于动作相关。\n【品类维度 -0.80】原始问题内容主体为《猫咪表情包梗图》,包含对象层「梗图、表情包」及场景层「猫咪、双标行为」。Sug词条《制作小房子》主体为「小房子」,与原始问题完全不匹配,品类完全无关。\n【延伸词维度 -0.60】sug词条「制作小房子」与原始问题「制作反映人类双标行为的猫咪表情包梗图」在主题、对象和目的上完全不相关,引入了与原始问题核心需求无关的全新概念,严重稀释了原始问题的聚焦度,导致内容偏离,属于作用域稀释型延伸词。\n【最终得分 -0.21】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.71
+    },
+    "sug_制作表格_r1_q1_9": {
+      "type": "sug",
+      "query": "[SUG] 制作表格",
+      "level": 13,
+      "relevance_score": -0.5650000000000001,
+      "evaluationReason": "【评估对象】词条\"制作表格\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 -0.70】原始问题的核心动机是围绕生成内容进行创作,旨在「制作」出一种特定形式的表情包梗图。sug词条「制作表格」虽然包含「制作」这一动词,但其指向的是「表格」的制作,与原始问题的「表情包梗图」的制作在行为目的和内容产出上存在显著差异,属于完全不同的制作行为,动作意图完全不匹配且方向相反。\n【品类维度 -0.50】原始问题核心对象是「表情包梗图」,场景是「人类双标行为」和「猫咪」。Sug词条核心对象为「表格」,品类完全不匹配,完全不同维度的内容主体。\n【延伸词维度 -0.15】sug词条「制作表格」与原始问题「制作反映人类双标行为的猫咪表情包梗图」的核心目的和作用域完全不符,引入了无关主题,稀释了原始问题的聚焦度,属于作用域稀释型。\n【最终得分 -0.57】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.71
+    },
+    "q_反映_r1_2": {
+      "type": "q",
+      "query": "[Q] 反映",
+      "level": 12,
+      "relevance_score": 0.024,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": 2,
+      "domain_type": "修饰短语"
+    },
+    "sug_反映问题还是反应问题_r1_q2_0": {
+      "type": "sug",
+      "query": "[SUG] 反映问题还是反应问题",
+      "level": 13,
+      "relevance_score": -0.7600000000000001,
+      "evaluationReason": "【评估对象】词条\"反映问题还是反应问题\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是「制作」表情包梗图。sug词条「反映问题还是反应问题」关注的是词语用法选择,没有明确的动作意图。\n【品类维度 -0.80】原始问题的核心是「猫咪表情包梗图」这一核心对象,限定词为「人类双标行为」。sug词为抽象的语法辨析「反映问题还是反应问题」,两者对象和限定词完全不匹配,品类冲突严重。\n【延伸词维度 -0.60】sug词条「反映问题还是反应问题」与原始问题「如何制作反映人类双标行为的猫咪表情包梗图」完全无关,属于作用域无关型,且严重稀释了原始问题的核心意图,导致负向评分。\n【最终得分 -0.76】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_反映和反应的区别_r1_q2_1": {
+      "type": "sug",
+      "query": "[SUG] 反映和反应的区别",
+      "level": 13,
+      "relevance_score": -0.35500000000000004,
+      "evaluationReason": "【评估对象】词条\"反映和反应的区别\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题的核心动机是「制作」,sug词条「反映和反应的区别」的核心动机是「辨析/理解」。两者动作完全不匹配。\n【品类维度 -0.85】原始问题主要涉及“猫咪表情包梗图”的制作,而sug词条是关于“反映和反应的区别”的语言知识。两者内容主体完全不相关,品类冲突严重,评分极低。\n【延伸词维度 -0.15】sug词条「反映和反应的区别」与原始问题「制作猫咪表情包梗图」的核心目的和作用域完全无关,属于无关型延伸词,且分散了用户注意力,稀释了原始问题的聚焦度。\n【最终得分 -0.36】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_反映是什么意思_r1_q2_2": {
+      "type": "sug",
+      "query": "[SUG] 反映是什么意思",
+      "level": 13,
+      "relevance_score": -0.04,
+      "evaluationReason": "【评估对象】词条\"反映是什么意思\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 -0.05】原始问题的核心动机是「制作」反映人类双标行为的猫咪表情包梗图。sug词条「反映是什么意思」的核心动机是「获取或解释」某种含义,二者动作方向不同。\n【品类维度 0.00】原始问题内容主体为《人类双标行为的猫咪表情包梗图》,sug词条内容主体为《反映》,二者无任何内容主体的关联性,属于不同品类,因此得分为0分0分处理。\n【延伸词维度 -0.15】sug词条「反映是什么意思」与原始问题「如何制作反映人类双标行为的猫咪表情包梗图」的核心目的和作用域完全不符,引入了无关的词义解释,严重稀释了原始问题的聚焦度。\n【最终得分 -0.04】",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_反映者_r1_q2_3": {
+      "type": "sug",
+      "query": "[SUG] 反映者",
+      "level": 13,
+      "relevance_score": 0.010000000000000009,
+      "evaluationReason": "【评估对象】词条\"反映者\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】sug词条「反映者」是纯名词,不包含任何动作意图,因此无法与原始问题的「制作」动机进行匹配。\n【品类维度 0.05】sug词是抽象的动词转名词,原始问题是一个具体的制作行为,两者相距甚远。sug词过度泛化,几乎无法体现原始问题中任何内容主体。\n【延伸词维度 -0.15】sug词条「反映者」与原始问题「如何制作反映人类双标行为的猫咪表情包梗图」的核心需求完全不符,引入了无关概念,稀释了原始问题的聚焦度,属于作用域稀释型。\n【最终得分 0.01】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_反应力小游戏_r1_q2_4": {
+      "type": "sug",
+      "query": "[SUG] 反应力小游戏",
+      "level": 13,
+      "relevance_score": -0.38000000000000006,
+      "evaluationReason": "【评估对象】词条\"反应力小游戏\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.00】原始问题的核心动机是“制作”梗图,而sug词条「反应力小游戏」的动作意图是“玩”/“进行”小游戏,两者动作意图完全不匹配。\n【品类维度 -0.80】原始问题核心对象是《猫咪表情包梗图》,场景是《人类双标行为》。sug词条核心对象是《小游戏》,场景是《反应力》。两者品类完全不相关,且词语含义偏离度大。\n【延伸词维度 -0.60】原始问题是关于“制作猫咪表情包梗图”的,核心是“制作”和“表情包梗图”;sug词条“反应力小游戏”与原始问题的“制作”和“表情包梗图”完全不相关,引入了全新的、不相关的概念,严重稀释了原始问题的聚焦度,属于作用域稀释型。\n【最终得分 -0.38】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_反应蛋白高是什么意思_r1_q2_5": {
+      "type": "sug",
+      "query": "[SUG] 反应蛋白高是什么意思",
+      "level": 13,
+      "relevance_score": -0.38000000000000006,
+      "evaluationReason": "【评估对象】词条\"反应蛋白高是什么意思\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题意图是「制作」表情包梗图,而sug词条「反应蛋白高是什么意思」的意图是「理解/获取知识」。两者动机完全不匹配。\n【品类维度 -0.80】原始问题是关于“猫咪表情包梗图”的制作,涉及娱乐创作领域。sug词条是“反应蛋白高是什么意思”,属于医学健康领域。两者内容主体完全不匹配,品类冲突,完全风马牛不相及。\n【延伸词维度 -0.60】sug词条「反应蛋白高是什么意思」与原始问题「如何制作反映人类双标行为的猫咪表情包梗图」在主题、内容和目的上完全不相关。sug词条引入了与原始问题制作表情包梗图毫无关联的医学概念,严重稀释了原始问题的聚焦度,导致内容完全偏离,属于作用域稀释型。\n【最终得分 -0.38】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_反映拼音_r1_q2_6": {
+      "type": "sug",
+      "query": "[SUG] 反映拼音",
+      "level": 13,
+      "relevance_score": -0.7100000000000001,
+      "evaluationReason": "【评估对象】词条\"反映拼音\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包、梗图)\n【动机维度 0.00】原始问题意图是「制作」表情包梗图,sug词条「反映拼音」无任何动作意图,因此无法匹配。\n【品类维度 -0.85】原始问题是关于《猫咪表情包梗图》的制作方法,限定词有《双标行为》,核心对象是《猫咪表情包梗图》。sug词条《反映拼音》与原始问题核心主体及所有限定词完全不符,品类严重冲突。\n【延伸词维度 -0.15】sug词条「反映拼音」与原始问题「如何制作反映人类双标行为的猫咪表情包梗图」的核心目的和作用域完全不符,引入了无关信息,严重稀释了原始问题的聚焦度。\n【最终得分 -0.71】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_反映财务状况的会计要素_r1_q2_7": {
+      "type": "sug",
+      "query": "[SUG] 反映财务状况的会计要素",
+      "level": 13,
+      "relevance_score": -0.8,
+      "evaluationReason": "【评估对象】词条\"反映财务状况的会计要素\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包、梗图)\n【动机维度 0.00】原始问题的核心动机是“制作”表情包或梗图,而sug词条“反映财务状况的会计要素”中没有明确的动作意图。sug词条缺失动机层,无法与原始问题的动机进行匹配。\n【品类维度 -0.85】原始问题对象层为「猫咪表情包梗图」,场景层为「反映人类双标行为」。Sug词条对象层为「会计要素」,场景层为「反映财务状况」。两者对象层和场景层完全不匹配,品类完全冲突,负相关。\n【延伸词维度 -0.60】sug词条中的“财务状况”和“会计要素”与原始问题中的“人类双标行为”和“猫咪表情包梗图”完全不相关,引入了与原始问题核心主题无关的全新概念,严重稀释了原始问题的聚焦度,导致内容偏离。\n【最终得分 -0.80】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_反映的英语_r1_q2_8": {
+      "type": "sug",
+      "query": "[SUG] 反映的英语",
+      "level": 13,
+      "relevance_score": -0.3,
+      "evaluationReason": "【评估对象】词条\"反映的英语\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包/梗图\n【动机维度 0.00】原始问题意图是「制作」反映人类双标行为的猫咪表情包/梗图,而sug词条「反映的英语」是指查询“反映”这个词的英文翻译,两者动作意图完全不相关。\n【品类维度 -0.60】原始问题意在制作「猫咪表情包梗图」,限定词为「人类双标行为」。sug词条「反映的英语」与原始问题在对象和场景上均完全错位,品类差异巨大。\n【延伸词维度 -0.60】sug词条「反映的英语」与原始问题「如何制作反映人类双标行为的猫咪表情包梗图」的核心目的和作用域完全不符,引入了无关的语言学习主题,严重稀释了原始问题的聚焦度,属于作用域稀释型延伸词。\n【最终得分 -0.30】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_反映事物间的互补关系_r1_q2_9": {
+      "type": "sug",
+      "query": "[SUG] 反映事物间的互补关系",
+      "level": 13,
+      "relevance_score": -0.26,
+      "evaluationReason": "【评估对象】词条\"反映事物间的互补关系\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题意图是「制作」表情包梗图,sug词表示「反映」互补关系。两者在行为意图上完全不一致,没有关联。sug词条的重点在于概念的「反映」,与原始问题的「制作」行为毫无关联。\n【品类维度 -0.50】原始问题的核心主体是「猫咪表情包梗图」及「双标行为」;sug词条「反映事物间的互补关系」与原始问题主体品类完全错位,无任何关联。\n【延伸词维度 -0.60】sug词条「反映事物间的互补关系」与原始问题「制作反映人类双标行为的猫咪表情包梗图」在主题、对象、目的上完全不相关,引入了全新的、不相干的概念,严重稀释了原始问题的聚焦度,导致内容偏离,属于作用域稀释型。\n【最终得分 -0.26】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "q_人类_r1_3": {
+      "type": "q",
+      "query": "[Q] 人类",
+      "level": 12,
+      "relevance_score": 0.024,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": 2,
+      "domain_type": "修饰短语"
+    },
+    "sug_人类一败涂地_r1_q3_0": {
+      "type": "sug",
+      "query": "[SUG] 人类一败涂地",
+      "level": 13,
+      "relevance_score": -0.6700000000000002,
+      "evaluationReason": "【评估对象】词条\"人类一败涂地\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包\n【动机维度 0.00】sug词条「人类一败涂地」是游戏名称,不包含任何动作意图,所以不存在动机匹配度。\n【品类维度 -0.80】原始问题核心是“猫咪表情包梗图”,限定词为“双标行为”。sug词条“人类一败涂地”与原始问题在对象层、场景层均不匹配,品类完全冲突。\n【延伸词维度 -0.15】sug词条「人类一败涂地」与原始问题「制作反映人类双标行为的猫咪表情包梗图」的核心目的和作用域完全不符,属于无关且稀释原始问题聚焦度的延伸,降低了内容的针对性。\n【最终得分 -0.67】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_人类狗窝_r1_q3_1": {
+      "type": "sug",
+      "query": "[SUG] 人类狗窝",
+      "level": 13,
+      "relevance_score": -0.19000000000000003,
+      "evaluationReason": "【评估对象】词条\"人类狗窝\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是“制作”猫咪表情包梗图,而sug词条“人类狗窝”是一个名词性短语,无法识别出任何动作意图,因此无法评估动机匹配度。\n【品类维度 -0.20】原始问题主对象是“猫咪表情包梗图”,限定词是“双标行为”。sug词《人类狗窝》是完全不相关的名词概念,无论是主体还是限定词都完全没有关联性极低且有误导性。\n【延伸词维度 -0.15】sug词条「人类狗窝」与原始问题「制作反映人类双标行为的猫咪表情包梗图」的核心概念和目的完全不符。它既不属于原始问题的任何作用域,也无法促进原始目的的达成,反而引入了无关且可能分散注意力的信息,属于作用域稀释型。\n【最终得分 -0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_人类幼崽陪伴指南_r1_q3_2": {
+      "type": "sug",
+      "query": "[SUG] 人类幼崽陪伴指南",
+      "level": 13,
+      "relevance_score": -0.52,
+      "evaluationReason": "【评估对象】词条\"人类幼崽陪伴指南\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题意在制作表情包,sug词条「人类幼崽陪伴指南」不含任何动作意图,且内容方向完全不匹配,因此动机维度评分为0。\n【品类维度 -0.50】原始问题核心是“猫咪表情包梗图”和“人类双标行为”,sug词条是“人类幼崽陪伴指南”,二者在主体对象上完全不匹配,品类冲突。\n【延伸词维度 -0.60】原始问题聚焦于「猫咪表情包梗图」的制作,延伸词「人类幼崽陪伴指南」引入了完全不相关的对象「人类幼崽」和动机「陪伴」,与原始问题的主题和目的完全脱节,属于作用域稀释型,且偏离度极高。\n【最终得分 -0.52】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_人类用沙想捏出梦里通天塔_r1_q3_3": {
+      "type": "sug",
+      "query": "[SUG] 人类用沙想捏出梦里通天塔",
+      "level": 13,
+      "relevance_score": -0.7600000000000001,
+      "evaluationReason": "【评估对象】词条\"人类用沙想捏出梦里通天塔\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图,意在表达/反映人类双标行为\n【动机维度 0.00】原始问题的核心动机是「制作」以「反映」某种行为的梗图。sug词条是描述一种人类行为,不包含任何动作意图,因此无法评估动机匹配度。\n【品类维度 -0.80】原始问题主体是「猫咪表情包梗图」、「人类双标行为」;sug词条主体是「人类」、「沙」、「通天塔」。二者在对象层和场景层均无任何交集,品类完全不相关。\n【延伸词维度 -0.60】sug词条「人类用沙想捏出梦里通天塔」与原始问题「如何制作反映人类双标行为的猫咪表情包梗图」在主题、内容和目的上完全不相关,属于作用域无关型,且严重偏离原始问题,导致稀释作用域。\n【最终得分 -0.76】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_人类幼仔_r1_q3_4": {
+      "type": "sug",
+      "query": "[SUG] 人类幼仔",
+      "level": 13,
+      "relevance_score": -0.7100000000000001,
+      "evaluationReason": "【评估对象】词条\"人类幼仔\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题意图是「制作」表情包梗图,sug词条「人类幼仔」是一个名词短语,没有包含任何动作意图,因此动机维度评分为0。\n【品类维度 -0.85】原始问题核心对象是《猫咪表情包梗图》,限定词有《人类双标行为》。sug词条《人类幼仔》与原始问题主体对象完全不符,且限定词也无关联,品类冲突。\n【延伸词维度 -0.15】原始问题聚焦于「猫咪表情包梗图」的制作,而延伸词「人类幼仔」引入了与猫咪无关的新对象,稀释了原始问题的核心主题,降低了内容的聚焦度。\n【最终得分 -0.71】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_人类简史_r1_q3_5": {
+      "type": "sug",
+      "query": "[SUG] 人类简史",
+      "level": 13,
+      "relevance_score": -0.6700000000000002,
+      "evaluationReason": "【评估对象】词条\"人类简史\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题意图是“制作”表情包梗图。sug词条《人类简史》指代一本书籍,没有明确的动作意图。\n【品类维度 -0.80】原始问题核心对象是“猫咪表情包梗图”和“双标行为”,场景限定为“人类”。sug词条“人类简史”核心对象为“简史”,场景为“人类”,二者对象层和场景层均完全不匹配,品类冲突,属于完全不相关的概念。\n【延伸词维度 -0.15】sug词条「人类简史」与原始问题「制作猫咪表情包梗图」的核心目的和作用域完全不符,属于无关且稀释主题的延伸,降低了内容的针对性。\n【最终得分 -0.67】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_人类进化史_r1_q3_6": {
+      "type": "sug",
+      "query": "[SUG] 人类进化史",
+      "level": 13,
+      "relevance_score": -0.12,
+      "evaluationReason": "【评估对象】词条\"人类进化史\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包/梗图\n【动机维度 0.00】原始问题意在「制作」表情包梗图,而sug词条「人类进化史」为纯名词,无任何动作意图,动机完全不匹配。\n【品类维度 0.00】原始问题核心是“猫咪表情包梗图”及“双标行为主题”。而sug词条“人类进化史”是历史类别,与原始问题在对象层和场景层均无任何关联。\n【延伸词维度 -0.60】原始问题聚焦于「制作反映人类双标行为的猫咪表情包梗图」,核心是「制作」和「猫咪表情包梗图」。sug词条「人类进化史」与原始问题在主题、目的和对象上均无关联,属于完全无关的延伸词,严重稀释了原始问题的聚焦度,且引入了完全不相干的信息,对原始目的达成有极强的负面影响。\n【最终得分 -0.12】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_人类跌落梦境_r1_q3_7": {
+      "type": "sug",
+      "query": "[SUG] 人类跌落梦境",
+      "level": 13,
+      "relevance_score": -0.6700000000000002,
+      "evaluationReason": "【评估对象】词条\"人类跌落梦境\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是「制作」表情包梗图。sug词条「人类跌落梦境」是一个游戏名称,没有体现任何制作的行为意图。\n【品类维度 -0.80】原始问题核心对象是“猫咪表情包梗图”,限定词是“人类双标行为”。sug词条“人类跌落梦境”是一个游戏名,与原始问题的核心对象和限定词均完全不匹配,品类完全冲突。\n【延伸词维度 -0.15】sug词条「人类跌落梦境」是一款游戏名称,与原始问题中「制作反映人类双标行为的猫咪表情包梗图」的核心目的和作用域完全不符,属于无关型延伸词,且分散了用户对核心需求的注意力。\n【最终得分 -0.67】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_人类群星闪耀时_r1_q3_8": {
+      "type": "sug",
+      "query": "[SUG] 人类群星闪耀时",
+      "level": 13,
+      "relevance_score": -0.436,
+      "evaluationReason": "【评估对象】词条\"人类群星闪耀时\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.00】sug词条「人类群星闪耀时」是一个作品名称,无法识别出任何动作意图,因此无法与原始问题的核心动作「制作」进行匹配。\n【品类维度 -0.50】原始问题的核心是「猫咪表情包梗图」、「双标行为」。sug词的核心是「人类群星闪耀时」。两者对象层和场景层均无任何关联,品类完全不同,属于品类冲突。\n【延伸词维度 -0.18】sug词条「人类群星闪耀时」与原始问题「制作反映人类双标行为的猫咪表情包梗图」在主题和目的上完全不相关,属于作用域无关型,且严重稀释了原始问题的聚焦度,导致内容偏离。\n【最终得分 -0.44】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_人类高质量男姓_r1_q3_9": {
+      "type": "sug",
+      "query": "[SUG] 人类高质量男姓",
+      "level": 13,
+      "relevance_score": -0.6760000000000002,
+      "evaluationReason": "【评估对象】词条\"人类高质量男姓\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是「制作」,sug词条「人类高质量男姓」是一个描述性名词短语,没有体现任何动作意图。因此,sug词条无法命中原始问题的动机。\n【品类维度 -0.80】原始问题核心对象为「猫咪表情包梗图」,限定词为「人类双标行为」。sug词条「人类高质量男性」与原始问题对象和限定词完全不匹配,品类冲突严重。\n【延伸词维度 -0.18】sug词条「人类高质量男姓」与原始问题「如何制作反映人类双标行为的猫咪表情包梗图」的核心主题和目的完全不符,属于无关且稀释原始问题焦点的延伸词,严重偏离了用户意图。\n【最终得分 -0.68】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "q_双标_r1_4": {
+      "type": "q",
+      "query": "[Q] 双标",
+      "level": 12,
+      "relevance_score": 0.024,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": 2,
+      "domain_type": "修饰短语"
+    },
+    "sug_双标是什么意思_r1_q4_0": {
+      "type": "sug",
+      "query": "[SUG] 双标是什么意思",
+      "level": 13,
+      "relevance_score": 0.0050000000000000044,
+      "evaluationReason": "【评估对象】词条\"双标是什么意思\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】「制作」猫咪表情包梗图,用于「反映」人类双标行为。核心动作是「制作」和「反映」\n【动机维度 0.00】原始问题的核心动机是「制作」和「反映」。sug词条「双标是什么意思」的核心动机是「了解/理解」。sug词条仅包含原始问题的主题「双标」,但没有围绕原始问题的核心动作「制作」或「反映」提供任何动机支持,因此动机不匹配。\n【品类维度 0.05】原始问题核心是《猫咪表情包梗图制作》,限定词是《人类双标行为》。sug词条仅包含《双标》,与原始问题的核心对象层《猫咪表情包梗图》几乎无关,覆盖度极低。\n【延伸词维度 -0.15】sug词条「双标是什么意思」中的「是什么意思」是延伸词,它将原始问题从「制作」行为转移到「解释」概念,稀释了原始问题制作梗图的聚焦度,降低了内容针对性。\n【最终得分 0.01】",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_讽刺双标的文案_r1_q4_1": {
+      "type": "sug",
+      "query": "[SUG] 讽刺双标的文案",
+      "level": 13,
+      "relevance_score": 0.010000000000000009,
+      "evaluationReason": "【评估对象】词条\"讽刺双标的文案\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作/生成(梗图)\n【动机维度 -0.15】原始问题的核心动机是「制作(梗图)」,sug词条的动机是「撰写/找寻(文案)」。两者动机存在部分重合,但方向上制作(图像)与撰写(文字)存在明显偏差。\n【品类维度 0.25】原始问题对象层为「猫咪表情包梗图」,场景层为「反映人类双标行为」。Sug词条对象层为「文案」,场景层为「讽刺双标」。场景层有部分匹配,但核心对象层「猫咪表情包梗图」与「文案」完全不符。\n【延伸词维度 -0.15】原始问题聚焦于「制作」猫咪表情包梗图,延伸词「文案」虽然与「讽刺双标」相关,但偏离了「制作」和「猫咪表情包」的核心对象,引入了不相关的创作形式,稀释了原始问题的聚焦度。\n【最终得分 0.01】",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_双标的人是什么心理_r1_q4_2": {
+      "type": "sug",
+      "query": "[SUG] 双标的人是什么心理",
+      "level": 13,
+      "relevance_score": -0.07999999999999999,
+      "evaluationReason": "【评估对象】词条\"双标的人是什么心理\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图),通过猫咪表情包梗图反映人类双标行为,旨在表达或讽刺\n【动机维度 0.00】原始问题的核心动机是「制作」反映特定主题的表情包梗图,带有「表达、讽刺」的意图。sug词条「双标的人是什么心理」不含显性动机,也无法推断出隐性动机。sug词条不包含任何动作意图,因此无法与原始问题的动机「制作」进行匹配。\n【品类维度 0.05】原始问题核心对象是《猫咪表情包梗图》,场景是《人类双标行为》。sug词条仅包含《双标》,且仅为泛化概念,与问题核心对象完全不对应,品类差异大。\n【延伸词维度 -0.60】sug词条「双标的人是什么心理」与原始问题「制作猫咪表情包梗图」的核心目的完全不符。原始问题是关于内容创作,而sug词条是心理学探讨,引入了完全不相关的概念,严重稀释了原始问题的聚焦度,导致负向评分。\n【最终得分 -0.08】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_双标文案_r1_q4_3": {
+      "type": "sug",
+      "query": "[SUG] 双标文案",
+      "level": 13,
+      "relevance_score": -0.23,
+      "evaluationReason": "【评估对象】词条\"双标文案\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题意图是「制作」表情包梗图,而sug词条「双标文案」未体现任何明确的动作意图。因此,sug词条无法匹配原始问题的动机。\n【品类维度 -0.25】原始问题是关于《猫咪表情包梗图》且反映《人类双标行为》,而sug词条是《双标文案》。sug词条无法命中核心对象,且限定词不同,属于品类错位。\n【延伸词维度 -0.15】原始问题聚焦于制作「猫咪表情包梗图」以反映「人类双标行为」,而sug词条「双标文案」将重点从「表情包梗图」转移到「文案」,且未提及「猫咪」元素,稀释了原始问题的核心对象和产出形式,属于作用域稀释型。\n【最终得分 -0.23】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_双标高爆卡点伴奏_r1_q4_4": {
+      "type": "sug",
+      "query": "[SUG] 双标高爆卡点伴奏",
+      "level": 13,
+      "relevance_score": -0.7100000000000001,
+      "evaluationReason": "【评估对象】词条\"双标高爆卡点伴奏\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包、梗图)\n【动机维度 0.00】原始问题的核心动机是「制作」表情包/梗图,而sug词条「双标高爆卡点伴奏」没有体现任何动作意图,因此动机不匹配。\n【品类维度 -0.85】原始问题对象层为「猫咪表情包梗图」,涉及「人类双标行为」限定词;sug词条对象层为「卡点伴奏」,主核心词与限定词均不匹配,品类完全冲突,负相关。\n【延伸词维度 -0.15】原始问题聚焦于「制作」反映「人类双标行为」的「猫咪表情包梗图」,强调内容创作和主题。sug词条「双标高爆卡点伴奏」中的「高爆卡点伴奏」与原始问题的「制作表情包梗图」这一核心目的和对象完全不符,引入了音乐制作和视频剪辑的无关维度,严重稀释了原始问题的聚焦度,导致内容偏离。\n【最终得分 -0.71】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_双标信用卡_r1_q4_5": {
+      "type": "sug",
+      "query": "[SUG] 双标信用卡",
+      "level": 13,
+      "relevance_score": -0.7600000000000001,
+      "evaluationReason": "【评估对象】词条\"双标信用卡\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包\n【动机维度 0.00】原始问题核心动机是「制作」图片作品,而sug词条「双标信用卡」无任何制作行为,sug词条无法识别动作意图,动机完全不匹配。\n【品类维度 -0.80】原始问题核心是「猫咪表情包梗图」,限定词为「反映人类双标行为」。Sug词「双标信用卡」的核心对象是「信用卡」,且限定词「双标」与原始问题中的「双标行为」含义完全不同,品类错位严重。\n【延伸词维度 -0.60】原始问题聚焦于「猫咪表情包梗图」的制作,延伸词「信用卡」与原始问题的核心主题完全无关,属于作用域无关型,且严重偏离原始问题,导致稀释作用域,故给予负分。\n【最终得分 -0.76】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_双椒兔做法_r1_q4_6": {
+      "type": "sug",
+      "query": "[SUG] 双椒兔做法",
+      "level": 13,
+      "relevance_score": -0.38000000000000006,
+      "evaluationReason": "【评估对象】词条\"双椒兔做法\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】sug词条「双椒兔做法」的动机是“如何制作/烹饪”,原始问题是“如何制作”,虽然都有制作,但是二者制作的客体完全不同,且sug词条中的‘做法’已经特指「食品」的制作方法,无法与原始问题意图相匹配。\n【品类维度 -0.80】原始问题的核心主体是“猫咪表情包梗图”和“双标行为”,sug词为“双椒兔做法”,对象完全不匹配,一个为食品制作,一个为文化创作,品类差异巨大。\n【延伸词维度 -0.60】sug词条「双椒兔做法」与原始问题「如何制作反映人类双标行为的猫咪表情包梗图」在主题、对象、目的上完全不相关,引入了与原始问题核心需求无关的全新概念,严重稀释了原始问题的聚焦度,导致内容完全偏离。\n【最终得分 -0.38】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_双标图片_r1_q4_7": {
+      "type": "sug",
+      "query": "[SUG] 双标图片",
+      "level": 13,
+      "relevance_score": 0.17,
+      "evaluationReason": "【评估对象】词条\"双标图片\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是“制作”表情包梗图,sug词条“双标图片”不包含任何动作意图,因此无法匹配。\n【品类维度 0.25】原始问题中包含核心对象“猫咪表情包梗图”和限定词“双标行为”。sug词条只包含了限定词“双标行为”的对象部分,缺失了核心对象“猫咪表情包梗图”,覆盖度较低。\n【延伸词维度 -0.15】原始问题聚焦于「猫咪表情包梗图」这一特定形式,而sug词条「图片」泛化了对象,稀释了原始问题的具体性和趣味性,属于作用域稀释型。\n【最终得分 0.17】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.024
+    },
+    "sug_双标表情包_r1_q4_8": {
+      "type": "sug",
+      "query": "[SUG] 双标表情包",
+      "level": 13,
+      "relevance_score": 0.4,
+      "evaluationReason": "【评估对象】词条\"双标表情包\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包\n【动机维度 0.00】sug词条「双标表情包」不包含明确的动作意图。原始问题的核心动机是「制作」,而sug词条仅提及了表情包的主题,未能体现出任何制作或获取表情包的动作。\n【品类维度 0.50】原始问题涉及“猫咪表情包梗图”和“双标行为”两个核心对象。sug词条“双标表情包”包含了“双标”和“表情包”,覆盖了原始问题大部分核心对象层,但缺失了“猫咪”这一关键限定词\n【延伸词维度 0.00】sug词条未引入延伸词,所有词汇均属于原始问题作用域范围。\n【最终得分 0.40】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.024
+    },
+    "sug_双标的人_r1_q4_9": {
+      "type": "sug",
+      "query": "[SUG] 双标的人",
+      "level": 13,
+      "relevance_score": 0.04000000000000001,
+      "evaluationReason": "【评估对象】词条\"双标的人\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是“制作”表情包梗图,sug词条「双标的人」只是一个名词短语,它本身不包含任何动作意图,也无法体现与“制作”相关的行为。\n【品类维度 0.05】原始问题核心对象是《猫咪表情包梗图》,限定词是《人类双标行为》。sug词《双标的人》虽然提及《双标》,但主体是《人》,与原始问题的核心对象《猫咪表情包梗图》完全不匹配,同时,sug词条只涉及原始问题的一小部分语义信息《双标》,导致匹配度较低。覆盖度极低。\n【延伸词维度 0.00】sug词条「双标的人」是原始问题「人类双标行为」的同义表达,属于原始问题作用域内的词汇,不构成延伸词。\n【最终得分 0.04】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "q_行为_r1_5": {
+      "type": "q",
+      "query": "[Q] 行为",
+      "level": 12,
+      "relevance_score": 0.024,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": 2,
+      "domain_type": "修饰短语"
+    },
+    "sug_行为心理学_r1_q5_0": {
+      "type": "sug",
+      "query": "[SUG] 行为心理学",
+      "level": 13,
+      "relevance_score": 0.010000000000000009,
+      "evaluationReason": "【评估对象】词条\"行为心理学\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是\"制作\",而sug词条「行为心理学」无明确的动作意图。因此,动机维度不匹配。\n【品类维度 0.05】原始问题核心对象为「猫咪表情包梗图」,限定词为「人类双标行为」。sug词条「行为心理学」是高度泛化的学科概念,其内容与「双标行为」有抽象关联,但与图片制作无直接主体关联,覆盖度极低。\n【延伸词维度 -0.15】原始问题聚焦于「制作」猫咪表情包梗图,而「行为心理学」是一个宽泛的学术领域,与具体的制作行为关联度低,稀释了原始问题的目的性。\n【最终得分 0.01】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_行为基础_r1_q5_1": {
+      "type": "sug",
+      "query": "[SUG] 行为基础",
+      "level": 13,
+      "relevance_score": 0.010000000000000009,
+      "evaluationReason": "【评估对象】词条\"行为基础\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是“制作”表情包梗图,而sug词条“行为基础”是一个名词短语,不包含任何动作意图,因此动机意图完全不匹配。\n【品类维度 0.05】原始问题是关于《人类双标行为》、《猫咪表情包梗图》的具体制作方法。sug词条《行为基础》过于抽象和泛化,无法构成有效内容主体匹配。\n【延伸词维度 -0.15】原始问题聚焦于「制作」猫咪表情包梗图,且主题是「人类双标行为」。sug词条「行为基础」是一个非常宽泛且抽象的词汇,与原始问题的具体制作行为和特定主题关联度极低,属于作用域稀释型延伸词,分散了原始问题的核心焦点。\n【最终得分 0.01】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_行为规范手抄报_r1_q5_2": {
+      "type": "sug",
+      "query": "[SUG] 行为规范手抄报",
+      "level": 13,
+      "relevance_score": -0.3350000000000001,
+      "evaluationReason": "【评估对象】词条\"行为规范手抄报\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图),通过制作表达观点\n【动机维度 0.00】原始问题的核心动机是「制作」具有特定表达意图的梗图。sug词条「行为规范手抄报」的动机是「制作」手抄报,虽然都包含「制作」的动作,但是意图载体和目的完全不匹配。\n【品类维度 -0.80】原始问题内容主体为《人类双标行为的猫咪表情包梗图》,作用域为『猫咪、表情包梗图、人类双标行为』;sug词条内容主体为《行为规范手抄报》,作用域为『行为规范、手抄报』。二者核心对象和限定词完全不同,品类高度不匹配。\n【延伸词维度 -0.15】原始问题聚焦于「猫咪表情包梗图」的制作,而sug词条「行为规范手抄报」引入了完全不相关的「手抄报」和「行为规范」概念,严重稀释了原始问题的核心主题和目的,属于作用域稀释型。\n【最终得分 -0.34】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_行为习惯手抄报_r1_q5_3": {
+      "type": "sug",
+      "query": "[SUG] 行为习惯手抄报",
+      "level": 13,
+      "relevance_score": -0.7100000000000001,
+      "evaluationReason": "【评估对象】词条\"行为习惯手抄报\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题意图是「制作」表情包梗图,而sug词条是「行为习惯手抄报」,未包含任何明确的动作意图。因此,sug词条与原始问题的动机无匹配。\n【品类维度 -0.85】原始问题的核心对象是“表情包梗图”,限定词为“猫咪”、“双标行为”,sug词条为“行为习惯手抄报”。两者对象类型完全不匹配,无任何共同限定词,品类冲突严重。\n【延伸词维度 -0.15】原始问题聚焦于「猫咪表情包梗图」的制作,强调「人类双标行为」这一主题。sug词条「行为习惯手抄报」中的「手抄报」与原始问题中的「表情包梗图」在形式上完全不符,且「行为习惯」也未能体现「人类双标行为」这一核心主题,属于引入无关信息,稀释了原始问题的聚焦度。\n【最终得分 -0.71】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_行为艺术_r1_q5_4": {
+      "type": "sug",
+      "query": "[SUG] 行为艺术",
+      "level": 13,
+      "relevance_score": -0.19000000000000003,
+      "evaluationReason": "【评估对象】词条\"行为艺术\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作梗图,用于反映人类双标行为\n【动机维度 0.00】原始问题的核心动机是「制作」梗图,sug词条「行为艺术」没有包含任何动作意图,因此无法评估动作匹配度。\n【品类维度 -0.20】原始问题侧重“猫咪表情包梗图”这一内容形式与“双标行为”这一具体主题,sug词条“行为艺术”虽与“行为”相关,但内容主体差异大,无法直接匹配,存在一定偏离。\n【延伸词维度 -0.15】原始问题聚焦于「猫咪表情包梗图」的制作,而「行为艺术」是一个宽泛的艺术形式,与制作表情包梗图的直接关联性较低,稀释了原始问题的具体制作目的。\n【最终得分 -0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_行为决定关系而非关系决定行为_r1_q5_5": {
+      "type": "sug",
+      "query": "[SUG] 行为决定关系而非关系决定行为",
+      "level": 13,
+      "relevance_score": -0.07,
+      "evaluationReason": "【评估对象】词条\"行为决定关系而非关系决定行为\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题意图是“制作”表情包梗图,sug词条「行为决定关系而非关系决定行为」无任何动作意图,无法评估动机匹配度。\n【品类维度 -0.05】原始问题核心对象是「猫咪表情包梗图」、「人类双标行为」,强调制作。sug词条是抽象的观点陈述,与原始问题无任何核心对象和场景匹配,存在语义错位,可能会引起用户迷惑,用户误解为制作表情包梗图而搜索。\n【延伸词维度 -0.15】sug词条「行为决定关系而非关系决定行为」与原始问题「如何制作反映人类双标行为的猫咪表情包梗图」的核心目的和作用域完全不符,属于无关型延伸词,且分散了用户对制作表情包的注意力,稀释了原始问题的聚焦度。\n【最终得分 -0.07】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_行为违反腾讯用户协议_r1_q5_6": {
+      "type": "sug",
+      "query": "[SUG] 行为违反腾讯用户协议",
+      "level": 13,
+      "relevance_score": -0.8,
+      "evaluationReason": "【评估对象】词条\"行为违反腾讯用户协议\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包\n【动机维度 0.00】原始问题的核心动机是「制作」表情包,而Sug词条「行为违反腾讯用户协议」并没有提出任何动作意图,因此无法匹配。\n【品类维度 -0.85】原始问题对象层为「猫咪表情包梗图」,场景层为「反映人类双标行为」。Sug词条与原始问题在对象层和场景层均完全不匹配,是完全不相关的安全提示。\n【延伸词维度 -0.60】原始问题聚焦于「制作猫咪表情包梗图」这一创意行为,而sug词条「行为违反腾讯用户协议」引入了与创作内容和方法完全无关的法律/平台规范维度,严重稀释了原始问题的核心目的和作用域,属于作用域稀释型延伸词。\n【最终得分 -0.80】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_行为认知疗法_r1_q5_7": {
+      "type": "sug",
+      "query": "[SUG] 行为认知疗法",
+      "level": 13,
+      "relevance_score": -0.6700000000000002,
+      "evaluationReason": "【评估对象】词条\"行为认知疗法\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图),通过制作来反映人类双标行为,表达创作意图\n【动机维度 0.00】原始问题的核心动机是「制作」带有特定主题的表情包梗图,而sug词条「行为认知疗法」中没有明确的动作语义,因此无法评估动作匹配度。\n【品类维度 -0.80】原始问题核心是“猫咪表情包梗图”,限定词是“人类双标行为”;sug词是“行为认知疗法”,对象和目的完全不同,品类错位严重。\n【延伸词维度 -0.15】原始问题聚焦于「制作表情包梗图」,而「行为认知疗法」是一个心理学概念,与表情包制作完全无关,属于无关型延伸词,且引入了完全不相关的专业领域,稀释了原始问题的聚焦度。\n【最终得分 -0.67】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_行为经济学_r1_q5_8": {
+      "type": "sug",
+      "query": "[SUG] 行为经济学",
+      "level": 13,
+      "relevance_score": -0.43000000000000005,
+      "evaluationReason": "【评估对象】词条\"行为经济学\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】sug词条「行为经济学」没有明确的动作意图,因此无法评估其与原始问题「制作」这一动作意图的匹配度。\n【品类维度 -0.50】原始问题核心是「人类双标行为」和「猫咪表情包梗图」。sug词是「行为经济学」,两者品类完全不匹配,核心对象和限定词均无关联。\n【延伸词维度 -0.15】sug词条「行为经济学」与原始问题「制作猫咪表情包梗图」的核心目的和作用域完全不符,属于无关且稀释主题的延伸,降低了内容的针对性。\n【最终得分 -0.43】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_行为规范家_r1_q5_9": {
+      "type": "sug",
+      "query": "[SUG] 行为规范家",
+      "level": 13,
+      "relevance_score": -0.19000000000000003,
+      "evaluationReason": "【评估对象】词条\"行为规范家\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】「制作」猫咪表情包梗图,用于「反映」人类双标行为\n【动机维度 0.00】原始问题明确意图为「制作」梗图,sug词条「行为规范家」无明确动作意图,且与「制作」、「反映」的核心动作完全不匹配。\n【品类维度 -0.20】原始问题核心对象是“猫咪表情包梗图”,限定词为“双标行为”。sug词为“行为规范家”,对象完全不符,且限定词无关联,无法体现人类双标行为,品类冲突。\n【延伸词维度 -0.15】原始问题是关于制作特定主题的表情包梗图,而“行为规范家”是一个抽象的概念,与表情包制作这一具体行为和猫咪、双标等主题均不相关。它引入了与原始问题核心目的无关的全新概念,稀释了原始问题的聚焦度,降低了内容针对性。\n【最终得分 -0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "q_猫咪_r1_6": {
+      "type": "q",
+      "query": "[Q] 猫咪",
+      "level": 12,
+      "relevance_score": 0.09,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": 3,
+      "domain_type": "中心名词"
+    },
+    "sug_猫咪领养免费领养_r1_q6_0": {
+      "type": "sug",
+      "query": "[SUG] 猫咪领养免费领养",
+      "level": 13,
+      "relevance_score": -0.27,
+      "evaluationReason": "【评估对象】词条\"猫咪领养免费领养\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作梗图\n【动机维度 0.00】原始问题的核心动机是「制作」梗图,而sug词条「猫咪领养免费领养」的动机是「领养」,两者动作意图完全无关。sug词条中包含猫咪,但主题无关。\n【品类维度 -0.55】原始问题涉及“猫咪+表情包梗图”,sug词为“猫咪领养”,两者对象层和场景层均完全不匹配,品类冲突。\n【延伸词维度 -0.50】原始问题聚焦于「制作表情包梗图」这一创意行为,对象是「猫咪」和「人类双标行为」。sug词条「猫咪领养免费领养」引入了「领养」这一全新且与原始问题核心目的完全无关的维度,严重稀释了原始问题的聚焦度,甚至将主题完全转向了另一个方向,因此给予负向评分。\n【最终得分 -0.27】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.09
+    },
+    "sug_猫咪叫声吸引小猫_r1_q6_1": {
+      "type": "sug",
+      "query": "[SUG] 猫咪叫声吸引小猫",
+      "level": 13,
+      "relevance_score": -0.26,
+      "evaluationReason": "【评估对象】词条\"猫咪叫声吸引小猫\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题意图是“制作”表情包梗图,sug词条“猫咪叫声吸引小猫”的意图是“吸引”(小猫),两者动作意图完全不相关。\n【品类维度 -0.50】原始问题核心对象为「表情包梗图」及「人类双标行为」,限定词为「猫咪」。sug词条核心对象为「猫咪叫声」及「小猫」,两者在核心对象和限定词上均不匹配,品类完全冲突。\n【延伸词维度 -0.60】sug词条中的“叫声”、“吸引”、“小猫”均与原始问题“制作反映人类双标行为的猫咪表情包梗图”的核心目的和作用域完全不符,引入了完全不相关的概念,严重稀释了原始问题的聚焦度,导致内容偏离。\n【最终得分 -0.26】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.09
+    },
+    "sug_猫咪呕吐_r1_q6_2": {
+      "type": "sug",
+      "query": "[SUG] 猫咪呕吐",
+      "level": 13,
+      "relevance_score": -0.436,
+      "evaluationReason": "【评估对象】词条\"猫咪呕吐\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包\n【动机维度 0.00】原始问题的核心动机是「制作」表情包,而sug词条「猫咪呕吐」没有明确的动作意图。因此,sug词条无法命中原始问题的动机。\n【品类维度 -0.50】原始问题核心对象为「猫咪表情包梗图」,限定词「反映人类双标行为」。Sug词条核心对象为「猫咪呕吐」,二者对象主体和限定词均不匹配,品类完全冲突。\n【延伸词维度 -0.18】sug词条「猫咪呕吐」与原始问题「制作反映人类双标行为的猫咪表情包梗图」的核心目的和作用域完全不符,引入了无关信息,严重稀释了原始问题的聚焦度,降低了内容的针对性。\n【最终得分 -0.44】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.09
+    },
+    "sug_猫咪头像_r1_q6_3": {
+      "type": "sug",
+      "query": "[SUG] 猫咪头像",
+      "level": 13,
+      "relevance_score": -0.19000000000000003,
+      "evaluationReason": "【评估对象】词条\"猫咪头像\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题意图是「制作」表情包梗图,而sug词条「猫咪头像」是纯名词,不包含任何动作意图,因此动机匹配度为0。\n【品类维度 -0.20】原始问题是关于『猫咪表情包梗图』,sug词条是『猫咪头像』。对象层「猫咪」匹配,但原始问题有更具体的「表情包梗图」这一核心对象,而sug词条是「头像」,存在品类错位,无法满足核心需求。\n【延伸词维度 -0.15】原始问题聚焦于「制作反映人类双标行为的猫咪表情包梗图」,强调制作、双标行为、表情包和梗图。sug词条「猫咪头像」与原始问题的核心目的「制作表情包梗图」关联度低,且「头像」与「表情包梗图」并非同义或细化关系,属于无关型延伸词。它稀释了原始问题中「制作」和「双标行为」的聚焦度,降低了内容的针对性。\n【最终得分 -0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.09
+    },
+    "sug_猫咪取名_r1_q6_4": {
+      "type": "sug",
+      "query": "[SUG] 猫咪取名",
+      "level": 13,
+      "relevance_score": -0.23500000000000004,
+      "evaluationReason": "【评估对象】词条\"猫咪取名\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包\n【动机维度 0.00】原始问题意图是「制作表情包」,而sug词条「猫咪取名」意图是「取名」。两者动作意图完全不匹配,且sug词条无明确动机层。因此,动机维度得分为0。\n【品类维度 -0.55】原始问题核心对象是《猫咪表情包梗图》,作用域包含《人类双标行为》。Sug词条核心对象为《猫咪取名》,两者对象完全错位,品类冲突严重,因此得分低。\n【延伸词维度 -0.15】原始问题聚焦于「制作反映人类双标行为的猫咪表情包梗图」,核心是「制作」和「表情包梗图」。「猫咪取名」与原始问题的核心目的和作用域完全不相关,引入了无关信息,稀释了原始问题的聚焦度,属于作用域稀释型。\n【最终得分 -0.24】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.09
+    },
+    "sug_猫咪品种_r1_q6_5": {
+      "type": "sug",
+      "query": "[SUG] 猫咪品种",
+      "level": 13,
+      "relevance_score": -0.43000000000000005,
+      "evaluationReason": "【评估对象】词条\"猫咪品种\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包\n【动机维度 0.00】原始问题意图是「制作」表情包,sug词条「猫咪品种」无任何动作意图,因此无法评估动作匹配度。\n【品类维度 -0.50】原始问题核心对象为「猫咪表情包梗图」及「双标行为」,限定词为「人类」。sug词条「猫咪品种」虽含「猫咪」,但将对象层从「表情包梗图」变为「品种」,完全不匹配,且缺失所有限定词。\n【延伸词维度 -0.15】原始问题聚焦于「制作」反映「双标行为」的「猫咪表情包梗图」,核心是创意和制作过程。「猫咪品种」与制作表情包梗图的核心目的和作用域无关,属于无关信息,稀释了原始问题的聚焦度。\n【最终得分 -0.43】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.09
+    },
+    "sug_猫咪搞笑视频_r1_q6_6": {
+      "type": "sug",
+      "query": "[SUG] 猫咪搞笑视频",
+      "level": 13,
+      "relevance_score": 0.010000000000000009,
+      "evaluationReason": "【评估对象】词条\"猫咪搞笑视频\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作梗图\n【动机维度 0.00】原始问题意图是「制作」反映人类双标行为的猫咪表情包梗图,核心动作是「制作」。sug词条「猫咪搞笑视频」没有包含制作的动作意图。因此,该词条与原始问题的动机不匹配。\n【品类维度 0.05】sug词条《猫咪搞笑视频》与原始问题《如何制作反映人类双标行为的猫咪表情包梗图》完全不同。原始问题是关于特定主题的表情包梗图,而sug词条是通用的搞笑视频,缺乏匹配的限定词和具体对象层。但有主体词《猫咪》,主题和形式都不一致。\n【延伸词维度 -0.15】原始问题聚焦于「制作」反映「人类双标行为」的「猫咪表情包梗图」。sug词条「猫咪搞笑视频」中的「搞笑视频」是延伸词,它将原始问题的「表情包梗图」这一对象层替换为「视频」,且「搞笑」这一动机层也与原始问题的「反映双标行为」这一深层动机不符,稀释了原始问题的核心目的和对象。\n【最终得分 0.01】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.09
+    },
+    "sug_猫咪叫声_r1_q6_7": {
+      "type": "sug",
+      "query": "[SUG] 猫咪叫声",
+      "level": 13,
+      "relevance_score": -0.43000000000000005,
+      "evaluationReason": "【评估对象】词条\"猫咪叫声\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题的核心动机是“制作”。sug词条“猫咪叫声”缺乏明确的动机意图,无法与原始问题的“制作”动作匹配。\n【品类维度 -0.50】原始问题核心是「猫咪表情包梗图」,场景为「反映人类双标行为」。sug词条「猫咪叫声」是完全不同的品类,与原始问题的核心对象和场景均不匹配,存在品类冲突。\n【延伸词维度 -0.15】原始问题聚焦于「制作反映人类双标行为的猫咪表情包梗图」,核心是创意和制作。sug词条「猫咪叫声」与原始问题的核心目的和作用域完全不符,属于无关信息,稀释了原始问题的聚焦度。\n【最终得分 -0.43】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.09
+    },
+    "sug_猫咪驱虫药推荐_r1_q6_8": {
+      "type": "sug",
+      "query": "[SUG] 猫咪驱虫药推荐",
+      "level": 13,
+      "relevance_score": -0.38000000000000006,
+      "evaluationReason": "【评估对象】词条\"猫咪驱虫药推荐\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.00】原始问题的核心动机是\"制作\"表情包梗图。sug词条\"猫咪驱虫药推荐\"的动机是\"获取推荐\"信息,与原始问题的\"制作\"动机完全不相关。\n【品类维度 -0.80】原始问题核心对象为「猫咪表情包梗图」及限定词「人类双标行为」,sug词条核心对象为「驱虫药」,限定词为「猫咪」,两者主体物品完全不一致,品类冲突严重。\n【延伸词维度 -0.60】原始问题聚焦于「制作表情包梗图」这一创意行为,对象是「猫咪」和「人类双标行为」。sug词条「猫咪驱虫药推荐」引入了与原始问题完全无关的「驱虫药」和「推荐」概念,属于作用域无关型延伸词,且与原始问题核心目的完全不符,严重稀释了原始问题的聚焦度,导致负向评分。\n【最终得分 -0.38】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.09
+    },
+    "sug_猫咪黑下巴怎么处理_r1_q6_9": {
+      "type": "sug",
+      "query": "[SUG] 猫咪黑下巴怎么处理",
+      "level": 13,
+      "relevance_score": -0.218,
+      "evaluationReason": "【评估对象】词条\"猫咪黑下巴怎么处理\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题意图是「制作」表情包梗图,sug词条意图是「处理」猫咪黑下巴问题。两者动作意图完全不匹配,且一个侧重内容创作,一个侧重问题解决方案。\n【品类维度 -0.50】原始问题对象层为「人类双标行为的猫咪表情包梗图」,场景层无。sug词条对象层为「猫咪黑下巴」,场景层无。两者对象层完全不同,品类错位。\n【延伸词维度 -0.18】原始问题聚焦于「制作」反映「人类双标行为」的「猫咪表情包梗图」,核心是创意和制作。sug词条「猫咪黑下巴怎么处理」引入了「猫咪健康护理」这一完全不相关的领域,与原始问题的「制作」和「双标行为」主题没有任何关联,严重稀释了原始问题的聚焦度,属于作用域稀释型。\n【最终得分 -0.22】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.09
+    },
+    "q_表情包_r1_7": {
+      "type": "q",
+      "query": "[Q] 表情包",
+      "level": 12,
+      "relevance_score": 0.15,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": 3,
+      "domain_type": "中心名词"
+    },
+    "sug_表情包抽象_r1_q7_0": {
+      "type": "sug",
+      "query": "[SUG] 表情包抽象",
+      "level": 13,
+      "relevance_score": 0.034,
+      "evaluationReason": "【评估对象】词条\"表情包抽象\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作梗图\n【动机维度 0.00】原始问题的核心动机是「制作」反映特定主题的梗图。sug词条「表情包抽象」仅为名词短语,无明确的动作意图,无法评估动作匹配度。\n【品类维度 0.08】原始问题意图明确且限定词清晰,对象层是『梗图』,限定词包括『人类双标行为』和『猫咪表情包』;sug词是『表情包抽象』,仅包含『表情包』这一个核心名词,且是抽象概念,与原问题具体概念差距大,覆盖度较低。\n【延伸词维度 -0.15】sug词条「抽象」与原始问题「制作反映人类双标行为的猫咪表情包梗图」的核心目的和对象均不匹配,稀释了原始问题的具体性和聚焦度,属于作用域稀释型。\n【最终得分 0.03】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.15
+    },
+    "sug_表情包怎么制作_r1_q7_1": {
+      "type": "sug",
+      "query": "[SUG] 表情包怎么制作",
+      "level": 13,
+      "relevance_score": 0.695,
+      "evaluationReason": "【评估对象】词条\"表情包怎么制作\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.98】原始问题核心动机是“制作”,sug词条「表情包怎么制作」的核心动机也是“制作”,两者核心动作完全一致,具备高度匹配性。\n【品类维度 0.55】原始问题对象层包含「表情包」「梗图」「猫咪」,场景层包含「双标行为」;sug词条仅包含对象层「表情包」。匹配度一般,因为sug词条只部分提及了核心对象,而大量相关限定词缺失。\n【延伸词维度 -0.15】sug词条「表情包怎么制作」仅提及制作方法,而原始问题明确了制作对象「猫咪表情包梗图」、主题「反映人类双标行为」,sug词条的延伸词「怎么制作」过于宽泛,稀释了原始问题的具体性和聚焦度,属于作用域稀释型。\n【最终得分 0.69】",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.15
+    },
+    "sug_表情包可爱_r1_q7_2": {
+      "type": "sug",
+      "query": "[SUG] 表情包可爱",
+      "level": 13,
+      "relevance_score": 0.010000000000000009,
+      "evaluationReason": "【评估对象】词条\"表情包可爱\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题的核心动机是「制作」,sug词条「表情包可爱」没有体现任何动作意图,因此无法评估动作匹配度。\n【品类维度 0.05】原始问题涉及“猫咪表情包梗图”,sug词为“表情包”,对象层有部分匹配,但sug词过度泛化且缺失了“猫咪”、“梗图”以及“双标行为”等所有限定词。\n【延伸词维度 -0.15】原始问题聚焦于「制作」反映「人类双标行为」的「猫咪表情包梗图」,强调内容和形式的特定性。「可爱」作为延伸词,与原始问题中的「双标行为」和「梗图」的核心目的不符,稀释了原始问题的聚焦度,降低了内容的针对性。\n【最终得分 0.01】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.15
+    },
+    "sug_表情包图片大全_r1_q7_3": {
+      "type": "sug",
+      "query": "[SUG] 表情包图片大全",
+      "level": 13,
+      "relevance_score": 0.17,
+      "evaluationReason": "【评估对象】词条\"表情包图片大全\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图),通过描绘猫咪来反映人类双标行为。\n【动机维度 0.00】原始问题的核心动机是\"制作\"某种具有特定主题和目的的表情包梗图。Sug词条\"表情包图片大全\"是纯名词短语,无任何动作意图,因此无法与原始问题的\"制作\"动作进行匹配,动机维度得分为0。\n【品类维度 0.25】sug词条仅包含核心对象“表情包图片”,但缺失了原始问题中所有的限定词和更具体的对象描述(人类双标行为、猫咪、梗图),覆盖度低,但品类仍相关。\n【延伸词维度 -0.15】原始问题聚焦于「制作」特定主题的「猫咪表情包梗图」,强调「人类双标行为」这一核心概念。sug词条「图片大全」与原始问题的「制作」和「特定主题」无直接关联,稀释了原始问题的核心目的和聚焦度,属于作用域稀释型。\n【最终得分 0.17】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.15
+    },
+    "sug_表情包搞笑配文_r1_q7_4": {
+      "type": "sug",
+      "query": "[SUG] 表情包搞笑配文",
+      "level": 13,
+      "relevance_score": -0.19,
+      "evaluationReason": "【评估对象】词条\"表情包搞笑配文\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 -0.15】原始问题的核心动机是「制作」反映人类双标行为的猫咪表情包梗图。sug词条「表情包搞笑配文」的隐含动机是「使用/寻找」搞笑配文,与原始问题的「制作」存在一定偏离,但两者都是围绕表情包展开动作。\n【品类维度 -0.25】原始问题涉及“猫咪表情包梗图”和“人类双标行为”的特定主题。sug词条「表情包搞笑配文」虽有“表情包”这一共同对象,但限定过于泛化,未体现「猫咪」和「双标行为」的核心场景与性质,且「配文」未完全匹配「梗图」的图文结合形式。\n【延伸词维度 -0.15】原始问题聚焦于「制作」反映「人类双标行为」的「猫咪表情包梗图」,强调了制作方法、主题和对象。sug词条「搞笑配文」虽然与表情包相关,但它将主题从「人类双标行为」泛化为「搞笑」,且将「制作」行为缩小为「配文」,稀释了原始问题的核心主题和制作的复杂性,属于作用域稀释型。\n【最终得分 -0.19】",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.15
+    },
+    "sug_表情包发给女朋友_r1_q7_5": {
+      "type": "sug",
+      "query": "[SUG] 表情包发给女朋友",
+      "level": 13,
+      "relevance_score": -0.12000000000000001,
+      "evaluationReason": "【评估对象】词条\"表情包发给女朋友\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 -0.05】原始问题是“制作”表情包梗图,而sug词条是“发给”表情包,两者都包含表情包,但原始问题为主动的制作行为,sug词条是分享行为,两者动作意图走向不同但有微弱关联。\n【品类维度 -0.20】原始问题需求的是制作「反映人类双标行为的猫咪表情包梗图」,而sug词条「表情包发给女朋友」限定了接收对象,失去了主体和客体以及限定词。\n【延伸词维度 -0.15】sug词条「表情包发给女朋友」中的“发给女朋友”是延伸词,它引入了原始问题未提及的社交场景和接收对象,稀释了原始问题关于“制作”和“内容”的核心聚焦,降低了内容的针对性。\n【最终得分 -0.12】",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.15
+    },
+    "sug_表情包简笔画_r1_q7_6": {
+      "type": "sug",
+      "query": "[SUG] 表情包简笔画",
+      "level": 13,
+      "relevance_score": 0.18,
+      "evaluationReason": "【评估对象】词条\"表情包简笔画\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包\n【动机维度 0.35】原始问题核心动机是「制作」反映双标行为的猫咪表情包梗图。sug词条「表情包简笔画」包含「表情包」这一对象和「简笔画」这一制作方式,与原始问题的「制作」动机有一定关联但语义不完全一致,因此为弱相关。\n【品类维度 0.05】原始问题对象层为「猫咪表情包梗图」,场景层为「人类双标行为」。Sug词条对象层为「表情包简笔画」。核心对象「表情包」匹配,但sug词条内容主体缺失「猫咪个体」及所有限定词,且泛化为「简笔画」\n【延伸词维度 -0.15】原始问题聚焦于「人类双标行为」和「猫咪表情包梗图」的特定主题创作,而sug词条「简笔画」引入了与原始问题主题和目的关联度较低的绘画风格,稀释了原始问题的核心创作意图,降低了内容的针对性。\n【最终得分 0.18】",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.15
+    },
+    "sug_表情包模板_r1_q7_7": {
+      "type": "sug",
+      "query": "[SUG] 表情包模板",
+      "level": 13,
+      "relevance_score": 0.034,
+      "evaluationReason": "【评估对象】词条\"表情包模板\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.00】原始问题是关于『制作』表情包梗图,而sug词条「表情包模板」没有明确的动作意图。因此,sug词条在动机维度上无法匹配。\n【品类维度 0.08】原始问题是关于“制作人类双标行为的猫咪表情包梗图”,sug词条仅有“表情包模板”,是宽泛概念,无法具体匹配原始问题的核心主体“猫咪表情包梗图”和限定词“人类双标行为”。仅在最泛化的对象层面上匹配,故得分较低。\n【延伸词维度 -0.15】原始问题聚焦于「制作反映人类双标行为的猫咪表情包梗图」这一具体创意和内容,而sug词条「表情包模板」仅提供了制作工具的通用素材,与原始问题的核心创意和内容关联度低,稀释了原始问题的聚焦度。\n【最终得分 0.03】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.15
+    },
+    "sug_表情包发给男朋友_r1_q7_8": {
+      "type": "sug",
+      "query": "[SUG] 表情包发给男朋友",
+      "level": 13,
+      "relevance_score": -0.14500000000000002,
+      "evaluationReason": "【评估对象】词条\"表情包发给男朋友\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作梗图\n【动机维度 -0.10】原始问题动机是“制作”梗图,而sug词条的动机是“发送/分享”表情包。两者虽然都与表情包相关,但动作意图存在方向性差异,制作是生成行为,发送是传播行为。\n【品类维度 -0.20】原始问题求“猫咪表情包梗图”,sug词条为“表情包”。sug词条对象层有“表情包”匹配,但限定词“猫咪”、“梗图”、“双标行为”缺失;sug词条加入新的限定词“男朋友”形成语义错位,故评分偏低。\n【延伸词维度 -0.15】延伸词“发给男朋友”引入了新的场景和对象,与原始问题“制作”表情包的核心目的和内容(人类双标行为的猫咪表情包梗图)无关,稀释了原始问题的聚焦度。\n【最终得分 -0.15】",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.15
+    },
+    "sug_表情包制作赚钱_r1_q7_9": {
+      "type": "sug",
+      "query": "[SUG] 表情包制作赚钱",
+      "level": 13,
+      "relevance_score": -0.16500000000000004,
+      "evaluationReason": "【评估对象】词条\"表情包制作赚钱\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作梗图\n【动机维度 -0.10】原始问题意图是「制作」表情包,sug词条意图是「制作」并「赚钱」。虽然都包含「制作」,但sug多了「赚钱」这个目的,导致动机方向略有偏离。\n【品类维度 -0.25】原始问题对象层为「表情包梗图」,场景层为「人类双标行为」和「猫咪」。Sug词仅对象层有「表情包」,但新增了意图「赚钱」,且限定词均不匹配,存在误导性。\n【延伸词维度 -0.15】原始问题聚焦于「制作反映人类双标行为的猫咪表情包梗图」这一特定内容和主题,而sug词条「赚钱」引入了商业变现的维度。这与原始问题的创作目的和内容方向无关,稀释了原始问题的聚焦度,使其偏离了核心的创作意图。\n【最终得分 -0.17】",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.15
+    },
+    "q_梗图_r1_8": {
+      "type": "q",
+      "query": "[Q] 梗图",
+      "level": 12,
+      "relevance_score": 0.024,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": 3,
+      "domain_type": "中心名词"
+    },
+    "sug_梗图素材_r1_q8_0": {
+      "type": "sug",
+      "query": "[SUG] 梗图素材",
+      "level": 13,
+      "relevance_score": 0.21000000000000002,
+      "evaluationReason": "【评估对象】词条\"梗图素材\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.00】原始问题意图是「制作」反映人类双标行为的猫咪表情包梗图。Sug词条「梗图素材」指向的是「获取素材」的动机,其本身无明确动词,无法与制作的动作匹配。\n【品类维度 0.50】sug词条《梗图素材》与原始问题中的核心对象词《梗图》匹配。原始问题中的《表情包》可以被《梗图》包含,作为一种具体的《梗图》类型。缺失了《猫咪》、《双标行为》等所有的限定词,但核心对象仍匹配。\n【延伸词维度 0.10】原始问题是制作梗图,sug词条「素材」是制作梗图的必要组成部分,属于作用域辅助型延伸词,对核心目的有辅助作用。\n【最终得分 0.21】",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.024
+    },
+    "sug_梗图搞笑_r1_q8_1": {
+      "type": "sug",
+      "query": "[SUG] 梗图搞笑",
+      "level": 13,
+      "relevance_score": 0.034,
+      "evaluationReason": "【评估对象】词条\"梗图搞笑\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题意图是“制作”梗图,而sug词条“梗图搞笑”无法识别明确的动作意图。sug词条缺失动机层,因此动机维度评分为0。\n【品类维度 0.08】原始问题核心对象为「猫咪表情包梗图」,场景为「反映人类双标行为」。sug词条「梗图搞笑」只泛化匹配到“梗图”,且缺失所有限定词,匹配度低。\n【延伸词维度 -0.15】原始问题聚焦于「制作」反映「人类双标行为」的「猫咪表情包梗图」,强调制作方法和内容主题。「搞笑」是梗图的普遍属性,但并非原始问题核心,且「梗图搞笑」未能体现原始问题中的「制作」和「人类双标行为」等关键信息,稀释了原始问题的聚焦度。\n【最终得分 0.03】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_梗图精神状态_r1_q8_2": {
+      "type": "sug",
+      "query": "[SUG] 梗图精神状态",
+      "level": 13,
+      "relevance_score": -0.19000000000000003,
+      "evaluationReason": "【评估对象】词条\"梗图精神状态\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是“制作”梗图,而sug词条“梗图精神状态”中没有明确的动作意图(即无法识别是制作、查找还是欣赏等)。\n【品类维度 -0.20】原始问题核心对象为「猫咪表情包梗图」,限定词较多。Sug词条「梗图精神状态」对象为「梗图」但限定词「精神状态」与原始问题语义不符且无法推断出「猫咪」限定,品类冲突。\n【延伸词维度 -0.15】原始问题聚焦于「制作」特定主题的「猫咪表情包梗图」,强调创作过程和具体内容。「精神状态」作为延伸词,与原始问题的核心「制作」和「猫咪表情包」无直接关联,且稀释了原始问题中「人类双标行为」这一具体主题的聚焦度,属于作用域稀释型。\n【最终得分 -0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_梗图meme_r1_q8_3": {
+      "type": "sug",
+      "query": "[SUG] 梗图meme",
+      "level": 13,
+      "relevance_score": 0.17,
+      "evaluationReason": "【评估对象】词条\"梗图meme\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包/梗图\n【动机维度 0.00】原始问题意图是「制作」反映特定主题的「猫咪表情包梗图」,而sug词条「梗图meme」只提及了对象「梗图」,没有包含任何动作意图,因此动机匹配度为0。\n【品类维度 0.25】原始问题对象层为「猫咪表情包梗图」,场景层为「反映人类双标行为」。Sug词条仅包含通用对象层「梗图」,缺失核心限定词「猫咪表情包」和所有场景层信息,覆盖度较低。\n【延伸词维度 -0.15】sug词条「梗图meme」中的「meme」是「梗图」的同义词,不构成延伸。但sug词条仅包含「梗图」,未能体现原始问题中「制作」、「人类双标行为」、「猫咪表情包」等核心要素,导致信息缺失,稀释了原始问题的聚焦度。\n【最终得分 0.17】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.024
+    },
+    "sug_梗图双人_r1_q8_4": {
+      "type": "sug",
+      "query": "[SUG] 梗图双人",
+      "level": 13,
+      "relevance_score": -0.19000000000000003,
+      "evaluationReason": "【评估对象】词条\"梗图双人\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是学习「制作」表情包梗图。平台sug词条「梗图双人」描述了梗图这一对象类型,但没有包含任何动机动作,因此动机维度评分为0。\n【品类维度 -0.20】原始问题核心对象为「猫咪表情包梗图」,限定词为「反映人类双标行为」。Sug词条虽然有「梗图」,但主体从「猫咪」变为「双人」,核心对象错位,且丢失限定词。\n【延伸词维度 -0.15】sug词条「双人」与原始问题「猫咪表情包梗图」的核心对象和制作目的无关,引入了不必要的限定,稀释了原始问题的聚焦度,属于作用域稀释型。\n【最终得分 -0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_梗图抽象_r1_q8_5": {
+      "type": "sug",
+      "query": "[SUG] 梗图抽象",
+      "level": 13,
+      "relevance_score": 0.034,
+      "evaluationReason": "【评估对象】词条\"梗图抽象\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.00】原始问题的核心动机是“制作梗图”,而sug词条“梗图抽象”中未包含明确的动词,无法识别其动作意图,因此动机匹配度为0。\n【品类维度 0.08】原始问题涉及“猫咪表情包梗图”,sug词条仅提到“梗图”,且主体是泛化词“抽象”。sug词过度泛化,与原始问题核心对象“猫咪表情包”差距较大。作用域覆盖度低。\n【延伸词维度 -0.15】原始问题聚焦于「制作反映人类双标行为的猫咪表情包梗图」这一具体内容和形式。sug词条「抽象」作为延伸词,与原始问题中的「双标行为」和「猫咪表情包」等具体内容无关,稀释了原始问题的聚焦度,降低了内容的针对性。\n【最终得分 0.03】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "sug_梗图描改_r1_q8_6": {
+      "type": "sug",
+      "query": "[SUG] 梗图描改",
+      "level": 13,
+      "relevance_score": 0.192,
+      "evaluationReason": "【评估对象】词条\"梗图描改\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作/生成(猫咪表情包梗图)\n【动机维度 0.35】原始问题意图是「制作」梗图,sug词条「梗图描改」中的「描改」是制作梗图的一种具体方法或过程,「描改」与「制作」在动作意图上存在一定的关联性,属于弱相关。\n【品类维度 0.08】原始问题涉及“猫咪表情包梗图”,sug词为“梗图描改”,sug词是制作梗图的一种方法,是制作层面的描述。主体词“梗图”匹配但缺乏其他限定,属于过度泛化,因此得分较低。\n【延伸词维度 -0.15】原始问题聚焦于「猫咪表情包梗图」的「制作」和「反映人类双标行为」这一主题。sug词条「描改」是制作梗图的一种具体方法,但它没有提及「猫咪表情包」和「人类双标行为」这两个核心对象和主题,稀释了原始问题的聚焦度,使其偏离了核心需求。\n【最终得分 0.19】",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.024
+    },
+    "sug_梗图模版_r1_q8_7": {
+      "type": "sug",
+      "query": "[SUG] 梗图模版",
+      "level": 13,
+      "relevance_score": 0.19400000000000003,
+      "evaluationReason": "【评估对象】词条\"梗图模版\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作/制作梗图\n【动机维度 0.00】原始问题询问的是如何“制作”梗图,包含明确的制作动作。sug词条“梗图模版”仅提供了制作梗图的辅助工具/对象,未能体现任何动作意图,因此动机匹配度为0。\n【品类维度 0.28】sug词条只包含对象层“梗图”,但原始问题的主体“猫咪双标行为表情包梗图”更为具体。sug词条只包含部分主体的泛化概念,缺失所有限定词,覆盖度低。\n【延伸词维度 -0.15】原始问题聚焦于「制作」特定主题的「猫咪表情包梗图」,强调创意和内容。sug词条「梗图模版」作为延伸词,虽然与「梗图」相关,但它将用户的需求从「制作」具体内容的创意过程,稀释为对通用「模版」的寻找。这偏离了原始问题中「反映人类双标行为」和「猫咪表情包」的特定主题和创作目的,降低了内容的针对性。\n【最终得分 0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.024
+    },
+    "sug_梗图分享_r1_q8_8": {
+      "type": "sug",
+      "query": "[SUG] 梗图分享",
+      "level": 13,
+      "relevance_score": 0.085,
+      "evaluationReason": "【评估对象】词条\"梗图分享\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题核心动机是“制作”,平台sug词条「梗图分享」的核心动机是“分享”,两者动机不匹配。sug词属于原始问题制作后的相关行为,但并非原始问题所需动机,故评分为0。\n【品类维度 0.25】sug词条《梗图分享》包含了原始问题中的核心对象《梗图》,但过度泛化且缺失了「反映人类双标行为的猫咪」这一所有限定词,匹配度一般。\n【延伸词维度 -0.15】原始问题聚焦于「制作」特定主题的「猫咪表情包梗图」,而sug词条「梗图分享」将重点从「制作」转移到「分享」,且未提及「猫咪」或「双标行为」等核心对象和主题,稀释了原始问题的目的和聚焦度。\n【最终得分 0.09】",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.024
+    },
+    "sug_梗图大全_r1_q8_9": {
+      "type": "sug",
+      "query": "[SUG] 梗图大全",
+      "level": 13,
+      "relevance_score": 0.034,
+      "evaluationReason": "【评估对象】词条\"梗图大全\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题的核心动机是“制作”,而sug词条“梗图大全”没有明确的动作意图。因此,sug词条与原始问题的动机不匹配。\n【品类维度 0.08】原始问题涉及“猫咪表情包梗图”,sug词为“梗图大全”。sug词是原始问题对象层的过度泛化,只涵盖了通用对象,未提及核心对象“猫咪表情包”及其他限定词。\n【延伸词维度 -0.15】原始问题聚焦于「制作」特定主题「猫咪表情包梗图」,而sug词条「梗图大全」引入了与「制作」无关的「大全」概念,且未提及「猫咪表情包」和「双标行为」等核心对象,稀释了原始问题的聚焦度。\n【最终得分 0.03】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 1,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.024
+    },
+    "step_comb_r1": {
+      "type": "step",
+      "query": "步骤2: 域内组合 (15个组合)",
+      "level": 11,
+      "relevance_score": 0,
+      "strategy": "域内组词",
+      "iteration": 1,
+      "is_selected": true
+    },
+    "comb_反映人类_r1_0": {
+      "type": "domain_combination",
+      "query": "反映人类",
+      "level": 12,
+      "relevance_score": 0.0702,
+      "evaluationReason": "【Round 1 域内评估】\n【评估对象】组合\"反映人类\" vs 作用域\"反映人类双标行为的\"\n【品类得分】0.78 - 核心主体“反映人类”完全匹配,但限定词“双标行为”缺失,属于核心主体匹配,存在限定词匹配的情况。\n【原始域得分】0.09\n【计算公式】品类得分 × 域得分 = 0.78 × 0.09\n【最终得分】0.07",
+      "strategy": "域内组合",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "[修饰短语]",
+      "source_words": [
+        [
+          "反映",
+          "人类"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        2
+      ],
+      "domains_str": "D2",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.024,
+      "scoreColor": "#22c55e"
+    },
+    "comb_反映双标_r1_1": {
+      "type": "domain_combination",
+      "query": "反映双标",
+      "level": 12,
+      "relevance_score": 0.081,
+      "evaluationReason": "【Round 1 域内评估】\n【评估对象】组合\"反映双标\" vs 作用域\"反映人类双标行为的\"\n【品类得分】0.90 - 核心主体“双标”和限定词“反映”均匹配,但缺少了“人类行为”这一限定,因此给予高分但非满分。\n【原始域得分】0.09\n【计算公式】品类得分 × 域得分 = 0.90 × 0.09\n【最终得分】0.08",
+      "strategy": "域内组合",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "[修饰短语]",
+      "source_words": [
+        [
+          "反映",
+          "双标"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        2
+      ],
+      "domains_str": "D2",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.024,
+      "scoreColor": "#22c55e"
+    },
+    "comb_反映行为_r1_2": {
+      "type": "domain_combination",
+      "query": "反映行为",
+      "level": 12,
+      "relevance_score": 0.0072,
+      "evaluationReason": "【Round 1 域内评估】\n【评估对象】组合\"反映行为\" vs 作用域\"反映人类双标行为的\"\n【品类得分】0.08 - sug词是通用概念“反映行为”,原始问题是特定概念“反映人类双标行为的”,通用概念不等于特定概念,因此品类不匹配,评分较低。\n【原始域得分】0.09\n【计算公式】品类得分 × 域得分 = 0.08 × 0.09\n【最终得分】0.01",
+      "strategy": "域内组合",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "[修饰短语]",
+      "source_words": [
+        [
+          "反映",
+          "行为"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        2
+      ],
+      "domains_str": "D2",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.024,
+      "scoreColor": "#ef4444"
+    },
+    "comb_人类双标_r1_3": {
+      "type": "domain_combination",
+      "query": "人类双标",
+      "level": 12,
+      "relevance_score": 0.081,
+      "evaluationReason": "【Round 1 域内评估】\n【评估对象】组合\"人类双标\" vs 作用域\"反映人类双标行为的\"\n【品类得分】0.90 - 核心主体'人类双标'完全匹配,限定词'行为的'在词条中被省略,但语义上高度相关,属于合理泛化。\n【原始域得分】0.09\n【计算公式】品类得分 × 域得分 = 0.90 × 0.09\n【最终得分】0.08",
+      "strategy": "域内组合",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "[修饰短语]",
+      "source_words": [
+        [
+          "人类",
+          "双标"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        2
+      ],
+      "domains_str": "D2",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.024,
+      "scoreColor": "#22c55e"
+    },
+    "comb_人类行为_r1_4": {
+      "type": "domain_combination",
+      "query": "人类行为",
+      "level": 12,
+      "relevance_score": 0.045,
+      "evaluationReason": "【Round 1 域内评估】\n【评估对象】组合\"人类行为\" vs 作用域\"反映人类双标行为的\"\n【品类得分】0.50 - 核心主体'人类行为'匹配,但限定词'双标'缺失,属于合理泛化。\n【原始域得分】0.09\n【计算公式】品类得分 × 域得分 = 0.50 × 0.09\n【最终得分】0.04",
+      "strategy": "域内组合",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "[修饰短语]",
+      "source_words": [
+        [
+          "人类",
+          "行为"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        2
+      ],
+      "domains_str": "D2",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.024,
+      "scoreColor": "#22c55e"
+    },
+    "comb_双标行为_r1_5": {
+      "type": "domain_combination",
+      "query": "双标行为",
+      "level": 12,
+      "relevance_score": 0.0765,
+      "evaluationReason": "【Round 1 域内评估】\n【评估对象】组合\"双标行为\" vs 作用域\"反映人类双标行为的\"\n【品类得分】0.85 - 核心主体词“双标行为”完全匹配,但原始问题中包含“反映人类的”这一限定词,sug词未包含,因此略有缺失。\n【原始域得分】0.09\n【计算公式】品类得分 × 域得分 = 0.85 × 0.09\n【最终得分】0.08",
+      "strategy": "域内组合",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "[修饰短语]",
+      "source_words": [
+        [
+          "双标",
+          "行为"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        2
+      ],
+      "domains_str": "D2",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.024,
+      "scoreColor": "#22c55e"
+    },
+    "comb_反映人类双标_r1_6": {
+      "type": "domain_combination",
+      "query": "反映人类双标",
+      "level": 12,
+      "relevance_score": 0.0882,
+      "evaluationReason": "【Round 1 域内评估】\n【评估对象】组合\"反映人类双标\" vs 作用域\"反映人类双标行为的\"\n【品类得分】0.98 - 核心主体和限定词完全匹配,仅缺少一个助词,语义完全一致。\n【原始域得分】0.09\n【计算公式】品类得分 × 域得分 = 0.98 × 0.09\n【最终得分】0.09",
+      "strategy": "域内组合",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "[修饰短语]",
+      "source_words": [
+        [
+          "反映",
+          "人类",
+          "双标"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        2
+      ],
+      "domains_str": "D2",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.024,
+      "scoreColor": "#22c55e"
+    },
+    "comb_反映人类行为_r1_7": {
+      "type": "domain_combination",
+      "query": "反映人类行为",
+      "level": 12,
+      "relevance_score": 0.0702,
+      "evaluationReason": "【Round 1 域内评估】\n【评估对象】组合\"反映人类行为\" vs 作用域\"反映人类双标行为的\"\n【品类得分】0.78 - 核心主体'人类行为'匹配,限定词'双标'在词条中缺失,但词条是同一作用域词条的合理泛化,因此给予较高分数。\n【原始域得分】0.09\n【计算公式】品类得分 × 域得分 = 0.78 × 0.09\n【最终得分】0.07",
+      "strategy": "域内组合",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "[修饰短语]",
+      "source_words": [
+        [
+          "反映",
+          "人类",
+          "行为"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        2
+      ],
+      "domains_str": "D2",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.024,
+      "scoreColor": "#22c55e"
+    },
+    "comb_反映双标行为_r1_8": {
+      "type": "domain_combination",
+      "query": "反映双标行为",
+      "level": 12,
+      "relevance_score": 0.0765,
+      "evaluationReason": "【Round 1 域内评估】\n【评估对象】组合\"反映双标行为\" vs 作用域\"反映人类双标行为的\"\n【品类得分】0.85 - 核心主体“双标行为”匹配,限定词“反映”匹配,但缺少“人类”这一限定词,因此给予较高但非满分评分。\n【原始域得分】0.09\n【计算公式】品类得分 × 域得分 = 0.85 × 0.09\n【最终得分】0.08",
+      "strategy": "域内组合",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "[修饰短语]",
+      "source_words": [
+        [
+          "反映",
+          "双标",
+          "行为"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        2
+      ],
+      "domains_str": "D2",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.024,
+      "scoreColor": "#22c55e"
+    },
+    "comb_人类双标行为_r1_9": {
+      "type": "domain_combination",
+      "query": "人类双标行为",
+      "level": 12,
+      "relevance_score": 0.081,
+      "evaluationReason": "【Round 1 域内评估】\n【评估对象】组合\"人类双标行为\" vs 作用域\"反映人类双标行为的\"\n【品类得分】0.90 - 核心主体“人类双标行为”完全匹配,但原始问题中的“反映”是动词,在品类维度评估中不予考虑,因此无法达到1.0分。\n【原始域得分】0.09\n【计算公式】品类得分 × 域得分 = 0.90 × 0.09\n【最终得分】0.08",
+      "strategy": "域内组合",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "[修饰短语]",
+      "source_words": [
+        [
+          "人类",
+          "双标",
+          "行为"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        2
+      ],
+      "domains_str": "D2",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.024,
+      "scoreColor": "#22c55e"
+    },
+    "comb_反映人类双标行为_r1_10": {
+      "type": "domain_combination",
+      "query": "反映人类双标行为",
+      "level": 12,
+      "relevance_score": 0.0882,
+      "evaluationReason": "【Round 1 域内评估】\n【评估对象】组合\"反映人类双标行为\" vs 作用域\"反映人类双标行为的\"\n【品类得分】0.98 - 核心主体和限定词完全匹配,仅缺少一个助词,语义完全一致。\n【原始域得分】0.09\n【计算公式】品类得分 × 域得分 = 0.98 × 0.09\n【最终得分】0.09",
+      "strategy": "域内组合",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "[修饰短语]",
+      "source_words": [
+        [
+          "反映",
+          "人类",
+          "双标",
+          "行为"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        2
+      ],
+      "domains_str": "D2",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.024,
+      "scoreColor": "#22c55e"
+    },
+    "comb_猫咪表情包_r1_11": {
+      "type": "domain_combination",
+      "query": "猫咪表情包",
+      "level": 12,
+      "relevance_score": 0.21059999999999998,
+      "evaluationReason": "【Round 1 域内评估】\n【评估对象】组合\"猫咪表情包\" vs 作用域\"猫咪表情包梗图\"\n【品类得分】0.90 - 核心主体“猫咪表情包”完全匹配,限定词“梗图”在词条中缺失,但“猫咪表情包”本身就常包含梗图,属于合理泛化,因此给予高分。\n【原始域得分】0.23\n【计算公式】品类得分 × 域得分 = 0.90 × 0.23\n【最终得分】0.21",
+      "strategy": "域内组合",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "[中心名词]",
+      "source_words": [
+        [
+          "猫咪",
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        3
+      ],
+      "domains_str": "D3",
+      "source_word_details": [
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.09,
+        0.15
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.15,
+      "scoreColor": "#22c55e"
+    },
+    "comb_猫咪梗图_r1_12": {
+      "type": "domain_combination",
+      "query": "猫咪梗图",
+      "level": 12,
+      "relevance_score": 0.21059999999999998,
+      "evaluationReason": "【Round 1 域内评估】\n【评估对象】组合\"猫咪梗图\" vs 作用域\"猫咪表情包梗图\"\n【品类得分】0.90 - 核心主体“猫咪梗图”完全匹配,限定词“表情包”缺失,但“梗图”本身就包含“表情包”的含义,属于合理泛化。\n【原始域得分】0.23\n【计算公式】品类得分 × 域得分 = 0.90 × 0.23\n【最终得分】0.21",
+      "strategy": "域内组合",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "[中心名词]",
+      "source_words": [
+        [
+          "猫咪",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        3
+      ],
+      "domains_str": "D3",
+      "source_word_details": [
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.09,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.09,
+      "scoreColor": "#22c55e"
+    },
+    "comb_表情包梗图_r1_13": {
+      "type": "domain_combination",
+      "query": "表情包梗图",
+      "level": 12,
+      "relevance_score": 0.1755,
+      "evaluationReason": "【Round 1 域内评估】\n【评估对象】组合\"表情包梗图\" vs 作用域\"猫咪表情包梗图\"\n【品类得分】0.75 - 核心主体'表情包梗图'完全匹配,但限定词'猫咪'缺失,属于合理泛化。\n【原始域得分】0.23\n【计算公式】品类得分 × 域得分 = 0.75 × 0.23\n【最终得分】0.18",
+      "strategy": "域内组合",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "[中心名词]",
+      "source_words": [
+        [
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        3
+      ],
+      "domains_str": "D3",
+      "source_word_details": [
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.15,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.15,
+      "scoreColor": "#22c55e"
+    },
+    "comb_猫咪表情包梗图_r1_14": {
+      "type": "domain_combination",
+      "query": "猫咪表情包梗图",
+      "level": 12,
+      "relevance_score": 0.23399999999999999,
+      "evaluationReason": "【Round 1 域内评估】\n【评估对象】组合\"猫咪表情包梗图\" vs 作用域\"猫咪表情包梗图\"\n【品类得分】1.00 - 核心主体“猫咪表情包梗图”与所有关键限定词完全匹配,达到完美匹配。\n【原始域得分】0.23\n【计算公式】品类得分 × 域得分 = 1.00 × 0.23\n【最终得分】0.23",
+      "strategy": "域内组合",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "[中心名词]",
+      "source_words": [
+        [
+          "猫咪",
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        3
+      ],
+      "domains_str": "D3",
+      "source_word_details": [
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.09,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.15,
+      "scoreColor": "#22c55e"
+    },
+    "step_search_r1": {
+      "type": "step",
+      "query": "步骤3: 筛选并执行搜索 (筛选2个高分词,搜索2次,undefined个帖子)",
+      "level": 11,
+      "relevance_score": 0,
+      "strategy": "筛选并执行搜索",
+      "iteration": 1,
+      "is_selected": true
+    },
+    "search_制作表情包_r1_0": {
+      "type": "search_word",
+      "query": "[SEARCH] 制作表情包",
+      "level": 12,
+      "relevance_score": 0.815,
+      "strategy": "搜索词",
+      "iteration": 1,
+      "is_selected": true
+    },
+    "post_66f9046a000000002a033202_0_0": {
+      "type": "post",
+      "query": "[R] 微信自制表情教程",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "66f9046a000000002a033202",
+      "note_url": "https://www.xiaohongshu.com/explore/66f9046a000000002a033202",
+      "body_text": "[拔草R]方法一:搜索即所得(图1-5) 用已有的表情包配上定制的文字,省心又省力[赞R] [拔草R]方法二:图库加表情",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g008318as11gc445g4be7ut8lflqs4df9uh8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008318as11gc44504be7ut8lflqskgfuu80?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008318as11gc44004be7ut8lflqsai35qm8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008318as11gc444g4be7ut8lflqshvdu428?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008318as11gc446g4be7ut8lflqslatfn98?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008318as11gc44304be7ut8lflqsg8kajfo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008318as11gc442g4be7ut8lflqs3rko7p0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008318as11gc44204be7ut8lflqsdnoccuo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008318as11gc441g4be7ut8lflqsq87bfm0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008318as11gc44404be7ut8lflqsj8g29qo?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008318as11gc445g4be7ut8lflqs4df9uh8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008318as11gc44504be7ut8lflqskgfuu80?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008318as11gc44004be7ut8lflqsai35qm8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008318as11gc444g4be7ut8lflqshvdu428?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008318as11gc446g4be7ut8lflqslatfn98?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008318as11gc44304be7ut8lflqsg8kajfo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008318as11gc442g4be7ut8lflqs3rko7p0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008318as11gc44204be7ut8lflqsdnoccuo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008318as11gc441g4be7ut8lflqsq87bfm0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008318as11gc44404be7ut8lflqsj8g29qo?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 3275,
+        "collected_count": 1284,
+        "comment_count": 201,
+        "shared_count": 1262
+      },
+      "extraction": null,
+      "is_knowledge": true,
+      "knowledge_reason": "帖子提供了制作微信表情包的具体方法和步骤,包括利用现有表情和从图库添加图片制作,是可复用的创作教程。",
+      "post_relevance_score": 0.5,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子提供了制作表情包的通用方法,这对于制作猫咪表情包梗图是可复用的基础知识。但它没有直接涉及“反映人类双标行为”这一内容主题,也未提供具体的梗图创作技巧,因此是中度相关。"
+    },
+    "post_668e06cd00000000250145fe_0_1": {
+      "type": "post",
+      "query": "[R] 如何把照片变成表情包!",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "668e06cd00000000250145fe",
+      "note_url": "https://www.xiaohongshu.com/explore/668e06cd00000000250145fe",
+      "body_text": "微信-我的-表情-设置-添加单个表情-添加 只能一个一个添加哦 #表情管理  #我是表情包  #每日表情包分享  #手机",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g2sg3151cqcmp0k5g5ph1r8choq4phkf6g9o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg3151cqcmp0k505ph1r8choq4pe27ihoo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg3151cqcmp0k4g5ph1r8choq4pvpkd1ro?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg3151cqcmp0k2g5ph1r8choq4pmbjtuqo?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3151cqcmp0k5g5ph1r8choq4phkf6g9o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3151cqcmp0k505ph1r8choq4pe27ihoo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3151cqcmp0k4g5ph1r8choq4pvpkd1ro?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3151cqcmp0k2g5ph1r8choq4pmbjtuqo?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 1416,
+        "collected_count": 720,
+        "comment_count": 108,
+        "shared_count": 390
+      },
+      "extraction": null,
+      "is_knowledge": true,
+      "knowledge_reason": "该帖子提供了将手机照片添加为微信表情包的具体操作步骤,包括菜单路径和添加方式,属于可复用的工具使用和流程指导。",
+      "post_relevance_score": 0.4,
+      "relevance_level": "中度相关",
+      "relevance_reason": "该帖子提供了将照片添加为表情包的通用方法,但原始问题是关于“制作反映人类双标行为的猫咪表情包梗图”。帖子内容不涉及“制作”梗图的创意、图片编辑、内容设计等核心知识,仅提供了上传已完成图片为表情包的最后一步,因此相关性较低。"
+    },
+    "post_68282bbd000000002001f349_0_2": {
+      "type": "post",
+      "query": "[R] 表情包P图素材抠图用",
+      "level": 13,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 1,
+      "is_selected": true,
+      "note_id": "68282bbd000000002001f349",
+      "note_url": "https://www.xiaohongshu.com/explore/68282bbd000000002001f349",
+      "body_text": "1️⃣醒图导入以上表情图,放大截图就行没水印 2️⃣再导入一张自己的图片,把五官抠图下来放进表情图 3️⃣调节: 亮度",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g2sg31hitil85jie05oni2p56dljo632j3dg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hitil85jieg5oni2p56dljof6v1da8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hitil85jif05oni2p56dljoj488ivg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hitil85jifg5oni2p56dljo7r8c1s8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hitil85jig05oni2p56dljokvtrd08?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hitil85jigg5oni2p56dljocns0amo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hitil85jih05oni2p56dljo6mdks3g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hitil85jihg5oni2p56dljo0nmkpg8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hitil85jii05oni2p56dljoj4dqga0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hitil85jiig5oni2p56dljo0a13c88?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hitil85jie05oni2p56dljo632j3dg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hitil85jieg5oni2p56dljof6v1da8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hitil85jif05oni2p56dljoj488ivg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hitil85jifg5oni2p56dljo7r8c1s8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hitil85jig05oni2p56dljokvtrd08?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hitil85jigg5oni2p56dljocns0amo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hitil85jih05oni2p56dljo6mdks3g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hitil85jihg5oni2p56dljo0nmkpg8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hitil85jii05oni2p56dljoj4dqga0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hitil85jiig5oni2p56dljo0a13c88?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 3070,
+        "collected_count": 1581,
+        "comment_count": 67,
+        "shared_count": 314
+      },
+      "extraction": null,
+      "is_knowledge": true,
+      "knowledge_reason": "帖子提供了利用“醒图”App进行表情包P图的具体步骤和技巧,包括导入、截图、抠图、调整等,是可复用的工具使用和创作方法指导。",
+      "post_relevance_score": 0.4,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子提供了制作表情包的基础P图方法,但未涉及如何将猫咪形象与人类双标行为结合来表达特定主题,缺乏创意层面的指导,仅提供了工具性知识,与原始问题的核心目标(反映人类双标行为)相关性弱。"
+    },
+    "post_67d1b6c9000000002802a550_0_3": {
+      "type": "post",
+      "query": "[R] 两眼一睁就是p,谁能懂我一下啊!!",
+      "level": 13,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 1,
+      "is_selected": true,
+      "note_id": "67d1b6c9000000002802a550",
+      "note_url": "https://www.xiaohongshu.com/explore/67d1b6c9000000002802a550",
+      "body_text": "谁懂!!给朋友p的专属龙图表情包真的太太太好用了[哇R][哇R][皱眉R]#龙图  #表情包  #表情包制作",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g2sg31eufppj6mid05puaiv1hi7vosk8e360?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31eufppj6me5g5puaiv1hi7vo634cf7o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31eufpprs6adg5puaiv1hi7von0q4ab0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31eufpplmmodg5puaiv1hi7vovr7cjro?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31eufppja6id05puaiv1hi7vo8j10o5g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31eufppja6o605puaiv1hi7voup7hvcg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831eufppj666605puaiv1hi7vo3oi99l0?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31eufppj6mid05puaiv1hi7vosk8e360?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31eufppj6me5g5puaiv1hi7vo634cf7o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31eufpprs6adg5puaiv1hi7von0q4ab0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31eufpplmmodg5puaiv1hi7vovr7cjro?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31eufppja6id05puaiv1hi7vo8j10o5g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31eufppja6o605puaiv1hi7voup7hvcg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831eufppj666605puaiv1hi7vo3oi99l0?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 330,
+        "collected_count": 105,
+        "comment_count": 5,
+        "shared_count": 27
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "帖子正文和图片未提供任何表情包制作方法、技巧、工具使用或流程指导,仅为个人分享制作成品及其使用感受。",
+      "post_relevance_score": 0.05,
+      "relevance_level": "低度相关",
+      "relevance_reason": "原始问题是关于如何制作反映人类双标行为的猫咪表情包梗图,而该帖子只是展示了“龙图”表情包成品及其个人感受,与猫咪主题和双标行为均不相关,也未提供任何制作方法,因此相关性极低。"
+    },
+    "post_68ef01ad000000000302f388_0_4": {
+      "type": "post",
+      "query": "[R] 企鹅空白表情包,就这样自制",
+      "level": 13,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 1,
+      "is_selected": true,
+      "note_id": "68ef01ad000000000302f388",
+      "note_url": "https://www.xiaohongshu.com/explore/68ef01ad000000000302f388",
+      "body_text": "#表情包  #抽象",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g00831nl36c1s6mag5pbs46co62bokf5ildo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831nl36c1s6m705pbs46co62bo81uu7bo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831nl36c1s6ma05pbs46co62bo7mgchdo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831nl36c1s6mb05pbs46co62bohqu3rao?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831nl36c1s6m7g5pbs46co62boc77qqp0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831nl36c1s6m9g5pbs46co62bou1kikd8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831nl36c1s6m805pbs46co62bog00p37o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831nl36c1s6m8g5pbs46co62bonva4cfg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831nl36c1s6m905pbs46co62bop2kook0?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831nl36c1s6mag5pbs46co62bokf5ildo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831nl36c1s6m705pbs46co62bo81uu7bo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831nl36c1s6ma05pbs46co62bo7mgchdo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831nl36c1s6mb05pbs46co62bohqu3rao?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831nl36c1s6m7g5pbs46co62boc77qqp0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831nl36c1s6m9g5pbs46co62bou1kikd8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831nl36c1s6m805pbs46co62bog00p37o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831nl36c1s6m8g5pbs46co62bonva4cfg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831nl36c1s6m905pbs46co62bop2kook0?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 296,
+        "collected_count": 122,
+        "comment_count": 12,
+        "shared_count": 33
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "帖子标题称“自制”,但实际内容仅展示了9张现成的企鹅表情包图片,没有提供任何关于如何制作或自制表情包的实用方法、技巧、工具使用或流程步骤指导。因此,不包含面向创作的内容知识。",
+      "post_relevance_score": 0.1,
+      "relevance_level": "低度相关",
+      "relevance_reason": "该帖子与原始问题“如何制作反映人类双标行为的猫咪表情包梗图”的相关性极低。帖子内容是企鹅表情包,而非猫咪表情包;仅提供了表情包图片,并非制作方法。更没有涉及“人类双标行为”这一主题,完全不能解决原始问题。"
+    },
+    "post_66ea4e30000000001e01964b_0_5": {
+      "type": "post",
+      "query": "[R] 教你DIY宝宝表情包~",
+      "level": 13,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 1,
+      "is_selected": true,
+      "note_id": "66ea4e30000000001e01964b",
+      "note_url": "https://www.xiaohongshu.com/explore/66ea4e30000000001e01964b",
+      "body_text": "亲妈会做的有趣小事儿之一 拍很多宝宝的小表情,做成微信表情包😊 三步教你完成✅ 🉑美图秀秀,选择“图片美化",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g008317sg969u3u705o4jptig9f90d1cgkpo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008317sg969uju005o4jptig9f90779odk0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008317sg969uju0g5o4jptig9f90dr2v328?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008317sg969uju105o4jptig9f90dm7ka6o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008317sg969uju1g5o4jptig9f90sn9d1d8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008317sg969uju205o4jptig9f90o1h9l0o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008317sg969uju2g5o4jptig9f90juhvb88?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008317sg969u3u705o4jptig9f90d1cgkpo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008317sg969uju005o4jptig9f90779odk0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008317sg969uju0g5o4jptig9f90dr2v328?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008317sg969uju105o4jptig9f90dm7ka6o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008317sg969uju1g5o4jptig9f90sn9d1d8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008317sg969uju205o4jptig9f90o1h9l0o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008317sg969uju2g5o4jptig9f90juhvb88?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 2802,
+        "collected_count": 1506,
+        "comment_count": 66,
+        "shared_count": 1137
+      },
+      "extraction": null,
+      "is_knowledge": true,
+      "knowledge_reason": "帖子提供了制作表情包的具体步骤、使用的工具(美图秀秀)和操作流程(编辑、文字、添加),是可复用的创作方法和教程。",
+      "post_relevance_score": 0.5,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子提供了制作表情包的方法,这与原始问题“如何制作表情包梗图”这一通用需求有部分重叠。然而,它专注于宝宝表情包,且未涉及“猫咪”、“双标行为”等主题,因此相关性中等偏低。"
+    },
+    "post_67c72549000000000602a376_0_6": {
+      "type": "post",
+      "query": "[R] 神图",
+      "level": 13,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 1,
+      "is_selected": true,
+      "note_id": "67c72549000000000602a376",
+      "note_url": "https://www.xiaohongshu.com/explore/67c72549000000000602a376",
+      "body_text": "#龙图  #龙图表情包  #制作表情包  #神图  可以用醒图做哟 方法如下:先导入图片,然后导入图片涂鸦你选的人物五官",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g00831ek5hebr10605p5g3g6amt105e4fh40?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31ek5heekh0dg5p5g3g6amt10vl3jgl8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31ek5heee0kdg5p5g3g6amt10fcpf5eg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831ek5hee8h06g5p5g3g6amt10hofg0l8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31ek5hedvgs6g5p5g3g6amt101bpf39o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31ek5heea0mk05p5g3g6amt10cbotjs8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831ek5hecgguk05p5g3g6amt103brrdm8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31ek5hec4gscg5p5g3g6amt10aqe8cng?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31ek5hecs0ucg5p5g3g6amt10nppf9vo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31ek5hec8h0dg5p5g3g6amt102v23658?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831ek5hec8106g5p5g3g6amt108o8v6qo?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831ek5hebr10605p5g3g6amt105e4fh40?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31ek5heekh0dg5p5g3g6amt10vl3jgl8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31ek5heee0kdg5p5g3g6amt10fcpf5eg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831ek5hee8h06g5p5g3g6amt10hofg0l8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31ek5hedvgs6g5p5g3g6amt101bpf39o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31ek5heea0mk05p5g3g6amt10cbotjs8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831ek5hecgguk05p5g3g6amt103brrdm8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31ek5hec4gscg5p5g3g6amt10aqe8cng?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31ek5hecs0ucg5p5g3g6amt10nppf9vo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31ek5hec8h0dg5p5g3g6amt102v23658?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831ek5hec8106g5p5g3g6amt108o8v6qo?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 5687,
+        "collected_count": 2915,
+        "comment_count": 270,
+        "shared_count": 586
+      },
+      "extraction": null,
+      "is_knowledge": true,
+      "knowledge_reason": "帖子提供了制作表情包的具体方法(使用醒图导入图片、涂鸦人物五官),包含可复用的工具使用和操作步骤。",
+      "post_relevance_score": 0.45,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子提供制作表情包的通用方法,这与原始问题中“制作…表情包梗图”这一部分有一定相关性。但它没有直接解决“反映人类双标行为的猫咪表情包梗图”的特定内容创作需求,未涉及创意、主题或猫咪元素。"
+    },
+    "post_6876f73f0000000011002f0c_0_7": {
+      "type": "post",
+      "query": "[R] 小表情包制作diy教程",
+      "level": 13,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 1,
+      "is_selected": true,
+      "note_id": "6876f73f0000000011002f0c",
+      "note_url": "https://www.xiaohongshu.com/explore/6876f73f0000000011002f0c",
+      "body_text": "1.醒图–人像–智能抠图抠一下,边边角角用快速画笔填充一下 2.美易–工具–调整大小,把抠出来的图改成萌萌的大小 这里统",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g2sg31jvrkm7537a04a5222l1n0jlb6odpq0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31jvrkm7537ag4a5222l1n0jlj151t30?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31jvrkm7537b04a5222l1n0jlhlk8a6o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31jvrkm7537bg4a5222l1n0jlrrvrc80?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31jvrkm7537c04a5222l1n0jlgq5ts3o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31jvrkm7537cg4a5222l1n0jlpaujoqg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31jvrkm7537d04a5222l1n0jlu4tntr8?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31jvrkm7537a04a5222l1n0jlb6odpq0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31jvrkm7537ag4a5222l1n0jlj151t30?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31jvrkm7537b04a5222l1n0jlhlk8a6o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31jvrkm7537bg4a5222l1n0jlrrvrc80?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31jvrkm7537c04a5222l1n0jlgq5ts3o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31jvrkm7537cg4a5222l1n0jlpaujoqg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31jvrkm7537d04a5222l1n0jlu4tntr8?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 333,
+        "collected_count": 152,
+        "comment_count": 11,
+        "shared_count": 9
+      },
+      "extraction": null,
+      "is_knowledge": true,
+      "knowledge_reason": "该帖子提供了使用“醒图”和“美易”两款工具制作表情包的具体步骤、操作方法和技巧,包含可复用的创作流程和指导。",
+      "post_relevance_score": 0.45,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子提供了制作表情包的基础方法和工具,对原始问题“如何制作…表情包”有一定参考价值。但其未涉及“反映人类双标行为”这一核心内容主题的创作构思,也未提供猫咪表情包的特定制作指导。"
+    },
+    "post_6613c01f000000001b00820c_0_8": {
+      "type": "post",
+      "query": "[R] 五步!🔥让你快速学会制作宝宝的表情包",
+      "level": 13,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 1,
+      "is_selected": true,
+      "note_id": "6613c01f000000001b00820c",
+      "note_url": "https://www.xiaohongshu.com/explore/6613c01f000000001b00820c",
+      "body_text": "乐乐出生后,妈妈手机里全是他的视频,想着视频占空间,想把视频制作成表情包,方便朋友和亲友之间转发。 静态表情包,按照下面",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g008311ave8gbmg005oslj57piakojpogkcg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008311ave8gbmg305oslj57piakoo0chl3g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008311ave8gbmg3g5oslj57piakogiir820?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008311ave8gbmg405oslj57piakovqkve20?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008311ave8gbmg4g5oslj57piakot7ht7l0?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008311ave8gbmg005oslj57piakojpogkcg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008311ave8gbmg305oslj57piakoo0chl3g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008311ave8gbmg3g5oslj57piakogiir820?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008311ave8gbmg405oslj57piakovqkve20?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008311ave8gbmg4g5oslj57piakot7ht7l0?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 3342,
+        "collected_count": 2600,
+        "comment_count": 90,
+        "shared_count": 835
+      },
+      "extraction": null,
+      "is_knowledge": true,
+      "knowledge_reason": "帖子提供了详细的表情包制作教程,包括工具使用(美图秀秀、剪映)、具体步骤(裁剪、加文字、导出)等,具备可复用指导性。",
+      "post_relevance_score": 0.5,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子提供了制作表情包的方法和工具,这与原始问题中“制作猫咪表情包梗图”的需求有通用性。然而,原始问题强调“反映人类双标行为”,这部分深层概念内容的创作指导在帖子中并未涉及,帖子侧重于技术层面而非创意内容,所以是中度相关。"
+    },
+    "post_61b81195000000002103b7ab_0_9": {
+      "type": "post",
+      "query": "[R] 自制表情包详细教程 | 一次过审 | 有手就行",
+      "level": 13,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 1,
+      "is_selected": true,
+      "note_id": "61b81195000000002103b7ab",
+      "note_url": "https://www.xiaohongshu.com/explore/61b81195000000002103b7ab",
+      "body_text": "感谢姐妹们的支持[飞吻R]教程来啦[害羞R] . . 💕图2:我们需要准备的素材如图,注意格式和图片尺寸。(如果不想接",
+      "images": [
+        "https://ci.xiaohongshu.com/5e86dcc2-4a10-34b1-b8aa-7ff0a00d9a4c?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1b97b7f3-f88a-3ef7-9c32-12c20399b114?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/ad49725f-1982-309a-8c35-dc6a809300d3?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/010276014j0xm9iyegd0115qjsu2io89ne?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/dad4aa54-8f4d-3f6c-9538-deae9b347f99?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/d4260339-157b-3139-81d0-83fb6ed664b2?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/f2e0aefc-5ace-30d5-8cc0-e5ac8e3e1738?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/2d306c8a-53ac-3a19-adfe-50d4e18768ce?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/56d82c8a-7089-355d-9dd9-330d26ab95f0?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/5e86dcc2-4a10-34b1-b8aa-7ff0a00d9a4c?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1b97b7f3-f88a-3ef7-9c32-12c20399b114?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/ad49725f-1982-309a-8c35-dc6a809300d3?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/010276014j0xm9iyegd0115qjsu2io89ne?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/dad4aa54-8f4d-3f6c-9538-deae9b347f99?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/d4260339-157b-3139-81d0-83fb6ed664b2?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/f2e0aefc-5ace-30d5-8cc0-e5ac8e3e1738?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/2d306c8a-53ac-3a19-adfe-50d4e18768ce?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/56d82c8a-7089-355d-9dd9-330d26ab95f0?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 99171,
+        "collected_count": 94670,
+        "comment_count": 978,
+        "shared_count": 8200
+      },
+      "extraction": null,
+      "is_knowledge": true,
+      "knowledge_reason": "该帖子提供了自制微信表情包的详细教程,包括素材准备、工具使用、图片格式和尺寸要求以及提交流程,是典型的可复用创作方法和步骤指导。",
+      "post_relevance_score": 0.45,
+      "relevance_level": "中度相关",
+      "relevance_reason": "该帖子提供了制作表情包的通用技术和流程,对于原始问题中“制作猫咪表情包梗图”这一创作行为有辅助作用。但它没有直接解决如何“反映人类双标行为”这一核心创意和内容策略问题,因此相关性中等偏低。"
+    },
+    "search_表情包怎么制作_r1_1": {
+      "type": "search_word",
+      "query": "[SEARCH] 表情包怎么制作",
+      "level": 12,
+      "relevance_score": 0.695,
+      "strategy": "搜索词",
+      "iteration": 1,
+      "is_selected": true
+    },
+    "step_next_round_r1": {
+      "type": "step",
+      "query": "步骤4: 构建下一轮 (25个查询)",
+      "level": 11,
+      "relevance_score": 0,
+      "strategy": "构建下一轮",
+      "iteration": 1,
+      "is_selected": true
+    },
+    "next_round_猫咪表情包梗图_r1_0": {
+      "type": "next_round_item",
+      "query": "[Q] 猫咪表情包梗图",
+      "level": 12,
+      "relevance_score": 0.23399999999999999,
+      "strategy": "域内组合",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "[中心名词]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_猫咪表情包_r1_1": {
+      "type": "next_round_item",
+      "query": "[Q] 猫咪表情包",
+      "level": 12,
+      "relevance_score": 0.21059999999999998,
+      "strategy": "域内组合",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "[中心名词]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_猫咪梗图_r1_2": {
+      "type": "next_round_item",
+      "query": "[Q] 猫咪梗图",
+      "level": 12,
+      "relevance_score": 0.21059999999999998,
+      "strategy": "域内组合",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "[中心名词]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_表情包梗图_r1_3": {
+      "type": "next_round_item",
+      "query": "[Q] 表情包梗图",
+      "level": 12,
+      "relevance_score": 0.1755,
+      "strategy": "域内组合",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "[中心名词]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_反映人类双标_r1_4": {
+      "type": "next_round_item",
+      "query": "[Q] 反映人类双标",
+      "level": 12,
+      "relevance_score": 0.0882,
+      "strategy": "域内组合",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "[修饰短语]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_反映人类双标行为_r1_5": {
+      "type": "next_round_item",
+      "query": "[Q] 反映人类双标行为",
+      "level": 12,
+      "relevance_score": 0.0882,
+      "strategy": "域内组合",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "[修饰短语]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_反映双标_r1_6": {
+      "type": "next_round_item",
+      "query": "[Q] 反映双标",
+      "level": 12,
+      "relevance_score": 0.081,
+      "strategy": "域内组合",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "[修饰短语]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_人类双标_r1_7": {
+      "type": "next_round_item",
+      "query": "[Q] 人类双标",
+      "level": 12,
+      "relevance_score": 0.081,
+      "strategy": "域内组合",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "[修饰短语]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_人类双标行为_r1_8": {
+      "type": "next_round_item",
+      "query": "[Q] 人类双标行为",
+      "level": 12,
+      "relevance_score": 0.081,
+      "strategy": "域内组合",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "[修饰短语]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_双标行为_r1_9": {
+      "type": "next_round_item",
+      "query": "[Q] 双标行为",
+      "level": 12,
+      "relevance_score": 0.0765,
+      "strategy": "域内组合",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "[修饰短语]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_反映双标行为_r1_10": {
+      "type": "next_round_item",
+      "query": "[Q] 反映双标行为",
+      "level": 12,
+      "relevance_score": 0.0765,
+      "strategy": "域内组合",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "[修饰短语]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_反映人类_r1_11": {
+      "type": "next_round_item",
+      "query": "[Q] 反映人类",
+      "level": 12,
+      "relevance_score": 0.0702,
+      "strategy": "域内组合",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "[修饰短语]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_反映人类行为_r1_12": {
+      "type": "next_round_item",
+      "query": "[Q] 反映人类行为",
+      "level": 12,
+      "relevance_score": 0.0702,
+      "strategy": "域内组合",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "[修饰短语]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_人类行为_r1_13": {
+      "type": "next_round_item",
+      "query": "[Q] 人类行为",
+      "level": 12,
+      "relevance_score": 0.045,
+      "strategy": "域内组合",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "[修饰短语]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_制作表情包_r1_14": {
+      "type": "next_round_item",
+      "query": "[Q] 制作表情包",
+      "level": 12,
+      "relevance_score": 0.815,
+      "strategy": "高增益SUG",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_表情包怎么制作_r1_15": {
+      "type": "next_round_item",
+      "query": "[Q] 表情包怎么制作",
+      "level": 12,
+      "relevance_score": 0.695,
+      "strategy": "高增益SUG",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_双标表情包_r1_16": {
+      "type": "next_round_item",
+      "query": "[Q] 双标表情包",
+      "level": 12,
+      "relevance_score": 0.4,
+      "strategy": "高增益SUG",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_梗图素材_r1_17": {
+      "type": "next_round_item",
+      "query": "[Q] 梗图素材",
+      "level": 12,
+      "relevance_score": 0.21000000000000002,
+      "strategy": "高增益SUG",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_梗图模版_r1_18": {
+      "type": "next_round_item",
+      "query": "[Q] 梗图模版",
+      "level": 12,
+      "relevance_score": 0.19400000000000003,
+      "strategy": "高增益SUG",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_梗图描改_r1_19": {
+      "type": "next_round_item",
+      "query": "[Q] 梗图描改",
+      "level": 12,
+      "relevance_score": 0.192,
+      "strategy": "高增益SUG",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_表情包简笔画_r1_20": {
+      "type": "next_round_item",
+      "query": "[Q] 表情包简笔画",
+      "level": 12,
+      "relevance_score": 0.18,
+      "strategy": "高增益SUG",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_双标图片_r1_21": {
+      "type": "next_round_item",
+      "query": "[Q] 双标图片",
+      "level": 12,
+      "relevance_score": 0.17,
+      "strategy": "高增益SUG",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_表情包图片大全_r1_22": {
+      "type": "next_round_item",
+      "query": "[Q] 表情包图片大全",
+      "level": 12,
+      "relevance_score": 0.17,
+      "strategy": "高增益SUG",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_梗图meme_r1_23": {
+      "type": "next_round_item",
+      "query": "[Q] 梗图meme",
+      "level": 12,
+      "relevance_score": 0.17,
+      "strategy": "高增益SUG",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_梗图分享_r1_24": {
+      "type": "next_round_item",
+      "query": "[Q] 梗图分享",
+      "level": 12,
+      "relevance_score": 0.085,
+      "strategy": "高增益SUG",
+      "iteration": 1,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "round_2": {
+      "type": "round",
+      "query": "Round 2",
+      "level": 20,
+      "relevance_score": 0,
+      "strategy": "第2轮",
+      "iteration": 2,
+      "is_selected": true
+    },
+    "step_sug_r2": {
+      "type": "step",
+      "query": "步骤1: 请求&评估推荐词 (179个)",
+      "level": 21,
+      "relevance_score": 0,
+      "strategy": "请求&评估推荐词",
+      "iteration": 2,
+      "is_selected": true
+    },
+    "q_制作表情包_r2_0": {
+      "type": "q",
+      "query": "[Q] 制作表情包",
+      "level": 22,
+      "relevance_score": 0.815,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": -1,
+      "domain_type": ""
+    },
+    "sug_制作表情包用什么软件_r2_q0_0": {
+      "type": "sug",
+      "query": "[SUG] 制作表情包用什么软件",
+      "level": 23,
+      "relevance_score": 0.31699999999999995,
+      "evaluationReason": "【评估对象】词条\"制作表情包用什么软件\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.60】原始问题是“制作”表情包梗图,sug词条是“制作”表情包用什么软件。sug词条的动作意图和原始问题一致,且提供了实现原始问题中「制作」这一动作所需要的工具辅助信息\n【品类维度 0.08】原始问题关心的是制作特定主题的猫咪表情包梗图,sug词条「制作表情包用什么软件」对象层仅为「表情包」,无限定词,且过度泛化,覆盖度低。\n【延伸词维度 -0.15】原始问题聚焦于「制作反映人类双标行为的猫咪表情包梗图」这一具体内容和主题,而sug词条「制作表情包用什么软件」引入了「软件」这一工具维度。虽然软件是制作表情包的辅助工具,但它偏离了原始问题对内容和主题的关注,稀释了原始问题的聚焦度,属于作用域稀释型延伸词。\n【最终得分 0.32】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.815
+    },
+    "sug_制作表情包微信_r2_q0_1": {
+      "type": "sug",
+      "query": "[SUG] 制作表情包微信",
+      "level": 23,
+      "relevance_score": 0.18,
+      "evaluationReason": "【评估对象】词条\"制作表情包微信\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.35】原始问题的核心动机是学习「制作」表情包。sug词条「制作表情包微信」也包含「制作」的动作意图。虽然sug词条只涉及制作的「方式/平台」(微信),未能涵盖原始问题更具体的对象(反映人类双标行为的猫咪表情包梗图),但动作意图是相关的。\n【品类维度 0.05】原始问题对象层为「猫咪表情包梗图」,场景层为「人类双标行为」。sug词条仅包含「制作表情包」,在对象层上仅部分匹配,缺乏「猫咪」和「梗图」限定,场景层完全缺失。内容主体覆盖度低。\n【延伸词维度 -0.15】延伸词「微信」引入了平台限制,与原始问题中制作表情包的核心目的无关,稀释了用户对表情包制作方法和创意的关注,属于作用域稀释型。\n【最终得分 0.18】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.815
+    },
+    "sug_制作表情包怎么赚钱_r2_q0_2": {
+      "type": "sug",
+      "query": "[SUG] 制作表情包怎么赚钱",
+      "level": 23,
+      "relevance_score": -0.17000000000000004,
+      "evaluationReason": "【评估对象】词条\"制作表情包怎么赚钱\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 -0.15】原始问题的核心动机是“制作”表情包,sug词条的动机是“赚钱”。两者都包含了“表情包”这个对象,但原始问题关注制作,sug词条关注赚钱,动机方向不同,属于轻度偏离。\n【品类维度 -0.20】原始问题核心对象是“猫咪表情包梗图”,限定词是“人类双标行为”;sug词条核心对象是“表情包”,限定词是“赚钱”。sug词条缺少原始核心对象“猫咪”和“梗图”,且限定词也完全不匹配,存在误导性。\n【延伸词维度 -0.15】sug词条「怎么赚钱」引入了原始问题未提及的商业化目的,稀释了原始问题「制作表情包」的创作和表达意图,属于作用域稀释型延伸词。\n【最终得分 -0.17】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.815
+    },
+    "sug_制作表情包教程_r2_q0_3": {
+      "type": "sug",
+      "query": "[SUG] 制作表情包教程",
+      "level": 23,
+      "relevance_score": 0.6799999999999999,
+      "evaluationReason": "【评估对象】词条\"制作表情包教程\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.95】原始问题的核心动机是「制作」表情包梗图,sug词条的动机也是「制作」表情包的「教程」,两者核心动作「制作」一致且sug词更加具象化为制作的教程,高度相关。\n【品类维度 0.05】原始问题对象层为“猫咪表情包梗图”,限定词为“反映人类双标行为”;sug词对象层为“表情包教程”,属于对象匹配但限定词缺失,通用度较高,评分较低。\n【延伸词维度 0.00】sug词条未引入延伸词,所有词汇均属于原始问题作用域范围。原始问题是关于“制作”表情包,sug词条“制作表情包教程”中的“教程”是对“制作”这一动机的细化,不属于延伸词。\n【最终得分 0.68】\n【规则说明】情况4:无延伸词,权重调整为 动机70% + 品类30%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.815
+    },
+    "sug_制作表情包软件_r2_q0_4": {
+      "type": "sug",
+      "query": "[SUG] 制作表情包软件",
+      "level": 23,
+      "relevance_score": 0.192,
+      "evaluationReason": "【评估对象】词条\"制作表情包软件\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.35】原始问题的核心动机是“制作”,sug词条“制作表情包软件”也包含了“制作”这一动作,但sug词条重心偏向于『制作所需工具』而非『制作内容或技巧』。虽然都是关于制作,但需求方向有差异,因此属于弱相关。\n【品类维度 0.08】原始问题核心对象是“猫咪表情包梗图”,限定词为“反映人类双标行为”;sug词是“制作表情包软件”。sug词过度泛化,仅包含原始问题中“制作”和“表情包”的泛化概念,缺失核心主体和所有限定词,匹配度低。\n【延伸词维度 -0.15】原始问题聚焦于「制作反映人类双标行为的猫咪表情包梗图」这一具体内容创作,而sug词条「制作表情包软件」引入了工具维度,稀释了原始问题对内容和创意的关注,降低了聚焦度。\n【最终得分 0.19】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.815
+    },
+    "sug_制作表情包gif_r2_q0_5": {
+      "type": "sug",
+      "query": "[SUG] 制作表情包gif",
+      "level": 23,
+      "relevance_score": 0.5599999999999999,
+      "evaluationReason": "【评估对象】词条\"制作表情包gif\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.95】原始问题的核心动机是「制作表情包梗图」,sug词条「制作表情包gif」的核心动机是「制作表情包」,两者核心动作与意图高度一致。原始问题对『表情包』的使用场景和内容进行了限定,但sug词条仍然保留了核心的动机动作。gif是表情包的一种表现形式,因此属于具体化范畴。\n【品类维度 0.25】sug词条「制作表情包gif」包含了原始问题的核心对象层元素“制作表情包”,但缺失了核心修饰性对象“猫咪表情包”。sug词的范围更泛化,覆盖度较低。\n【延伸词维度 -0.15】原始问题聚焦于「猫咪表情包梗图」的内容创作,而sug词条「gif」引入了新的格式维度,稀释了原始问题对内容和主题的关注,降低了聚焦度。\n【最终得分 0.56】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.815
+    },
+    "sug_制作表情包动图_r2_q0_6": {
+      "type": "sug",
+      "query": "[SUG] 制作表情包动图",
+      "level": 23,
+      "relevance_score": 0.492,
+      "evaluationReason": "【评估对象】词条\"制作表情包动图\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.95】原始问题的核心动机是「制作」表情包梗图。sug词条的动机也是「制作」表情包动图。虽然一个是梗图一个是动图,但「制作」动作的语义高度一致,且都指向表情包。\n【品类维度 0.08】原始问题对象层为「猫咪表情包梗图」,场景层为「反映人类双标行为」。Sug词条仅包含「表情包动图」这一泛化的对象,未提及具体主体「猫咪」及限定词「双标行为」。覆盖度极低。\n【延伸词维度 -0.15】原始问题聚焦于“梗图”这一静态或多格图片形式,而sug词条引入了“动图”这一新的表现形式,稀释了原始问题的聚焦度,降低了内容的针对性。\n【最终得分 0.49】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.815
+    },
+    "sug_视频制作表情包_r2_q0_7": {
+      "type": "sug",
+      "query": "[SUG] 视频制作表情包",
+      "level": 23,
+      "relevance_score": 0.43,
+      "evaluationReason": "【评估对象】词条\"视频制作表情包\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包\n【动机维度 0.85】原始问题和sug词条的核心动作意图都是“制作表情包”,具有高度的匹配性。sug词提供了“视频”这种制作方式,是原始意图的子集或具体化场景。\n【品类维度 0.05】原始问题核心对象是《猫咪表情包梗图》,限定词有《人类双标行为》。sug词条核心对象是《表情包》,是原始问题核心对象的泛化,但完全缺失核心限定词,且存在对象错位(视频制作)。\n【延伸词维度 -0.15】原始问题聚焦于「猫咪表情包梗图」的制作,而sug词条引入了「视频」这一新的载体形式,稀释了原始问题对「梗图」这一特定形式的聚焦,降低了内容的针对性。\n【最终得分 0.43】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.815
+    },
+    "sug_美图秀秀制作表情包图片_r2_q0_8": {
+      "type": "sug",
+      "query": "[SUG] 美图秀秀制作表情包图片",
+      "level": 23,
+      "relevance_score": 0.43,
+      "evaluationReason": "【评估对象】词条\"美图秀秀制作表情包图片\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.85】原始问题的核心动机是「制作」表情包梗图。sug词条明确提到了「制作」表情包图片,虽然对象、场景和目的有所差异,但核心制作动作是高度匹配的。\n【品类维度 0.05】原始问题对象层为《猫咪表情包梗图》,场景层为《反映人类双标行为》。sug词条对象层为《表情包图片》。对象层部分匹配。sug词条完全缺失所有场景层限定,对象层匹配度低,但属于相关品类,故分数较低。\n【延伸词维度 -0.15】延伸词“美图秀秀”和“图片”与原始问题“制作反映人类双标行为的猫咪表情包梗图”的核心目的和内容无关,引入了不必要的工具和通用概念,稀释了原始问题的聚焦度,降低了内容的针对性。\n【最终得分 0.43】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.815
+    },
+    "sug_制作表情包文字_r2_q0_9": {
+      "type": "sug",
+      "query": "[SUG] 制作表情包文字",
+      "level": 23,
+      "relevance_score": 0.44199999999999995,
+      "evaluationReason": "【评估对象】词条\"制作表情包文字\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】用户想要「制作」表情包梗图,核心动作是创作或生成一个具体产物。\n【动机维度 0.85】原始问题的核心动机是「制作」表情包梗图,sug词条「制作表情包文字」的动词「制作」与原始问题完全一致,匹配度极高。尽管sug词条限定了制作「文字」,但「制作」这一核心动作和方向完全一致。\n【品类维度 0.08】原始问题核心对象层为「猫咪表情包梗图」,场景层包括「双标行为」和「人类」。sug词条「制作表情包文字」仅模糊包含「表情包」对象层,限定词「文字」与原始问题「梗图」有差异,主体元素大部分缺失。\n【延伸词维度 -0.15】延伸词「文字」与原始问题「猫咪表情包梗图」的核心对象不符,稀释了原始问题对图像和特定主题(猫咪、双标行为)的聚焦,降低了内容针对性。\n【最终得分 0.44】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.815
+    },
+    "q_表情包怎么制作_r2_1": {
+      "type": "q",
+      "query": "[Q] 表情包怎么制作",
+      "level": 22,
+      "relevance_score": 0.695,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": -1,
+      "domain_type": ""
+    },
+    "sug_表情包怎么制作gif_r2_q1_0": {
+      "type": "sug",
+      "query": "[SUG] 表情包怎么制作gif",
+      "level": 23,
+      "relevance_score": 0.33,
+      "evaluationReason": "【评估对象】词条\"表情包怎么制作gif\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包\n【动机维度 0.65】原始问题核心动机是「制作」表情包,sug词条是「制作」gif。gif是表情包的常见形式,制作gif可以服务于制作表情包,二者动作意图高度相关。\n【品类维度 0.05】原始问题核心是「猫咪表情包梗图」这一特定类型内容。sug词仅有「表情包」这一泛化对象层,缺少核心限定词「猫咪」、「梗图」以及行为层面描述。二者品类存在代沟。\n【延伸词维度 -0.15】原始问题聚焦于「猫咪表情包梗图」的内容和主题,而sug词条「gif」引入了无关的格式限定,稀释了原始问题的核心内容,属于作用域稀释型。\n【最终得分 0.33】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.695
+    },
+    "sug_表情包怎么制作动态_r2_q1_1": {
+      "type": "sug",
+      "query": "[SUG] 表情包怎么制作动态",
+      "level": 23,
+      "relevance_score": 0.272,
+      "evaluationReason": "【评估对象】词条\"表情包怎么制作动态\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.35】原始问题意图是「制作」反映特定主题的「梗图」,sug词条意图是「制作」能「动态」的「表情包」。两者核心动作「制作」一致,但sug词条具体化为「动态表情包」,原始问题则侧重「梗图」,并包含更复杂的表意需求,因此是弱相关。\n【品类维度 0.28】原始问题对象层为「猫咪表情包梗图」,场景层为「反映人类双标行为」。Sug词条对象层为「动态表情包制作」。Sug词条仅命中表情包,但限定词“动态”与原问题场景不符,且对象层缺失“猫咪”和“梗图”限定”。\n【延伸词维度 -0.15】原始问题聚焦于「猫咪表情包梗图」的「制作」方法,并强调其「反映人类双标行为」这一主题。sug词条「动态」引入了新的制作形式,与原始问题中未提及的「动态」制作方式不符,稀释了原始问题对特定主题和内容的聚焦,属于作用域稀释型。\n【最终得分 0.27】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.695
+    },
+    "sug_表情包怎么制作微信_r2_q1_2": {
+      "type": "sug",
+      "query": "[SUG] 表情包怎么制作微信",
+      "level": 23,
+      "relevance_score": 0.31,
+      "evaluationReason": "【评估对象】词条\"表情包怎么制作微信\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.45】原始问题的核心动机是\"制作\"表情包。sug词条的动机也是\"制作\"表情包,但原始问题中的制作更加具体,指向制作梗图。两者都是制作表情包,但侧重点不同。\n【品类维度 0.25】原始问题对象层为「猫咪表情包梗图」,场景层为「人类双标行为」。sug词条只涉及「表情包」这一部分对象,但缺少核心对象「猫咪」和所有限定词,覆盖度低。\n【延伸词维度 -0.15】原始问题聚焦于「人类双标行为的猫咪表情包梗图」的制作方法,而sug词条「微信」引入了新的平台限定,稀释了原始问题的核心内容,使其偏离了对表情包内容和主题的关注。\n【最终得分 0.31】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.695
+    },
+    "sug_表情包怎么制作文字_r2_q1_3": {
+      "type": "sug",
+      "query": "[SUG] 表情包怎么制作文字",
+      "level": 23,
+      "relevance_score": 0.455,
+      "evaluationReason": "【评估对象】词条\"表情包怎么制作文字\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.90】原始问题的核心动机是学习「制作」表情包梗图。sug词条「表情包怎么制作文字」的核心动机是学习「制作」表情包。两者的核心动作「制作」完全一致,只是sug词条限定了制作的内容为「文字」\n【品类维度 0.05】原始问题对象层为「猫咪表情包梗图」,场景层为「反映人类双标行为」。Sug词条对象层为「表情包」,无限定词。核心对象层部分匹配,但限定词全部缺失且语义错位。泛化相似。\n【延伸词维度 -0.15】原始问题聚焦于「猫咪表情包梗图」的「内容创意」(反映人类双标行为),而sug词条「文字」是表情包制作的通用元素,与原始问题特指的「猫咪」和「双标行为」内容无关,稀释了原始问题的核心内容和创意目的。\n【最终得分 0.46】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.695
+    },
+    "sug_小红书表情包怎么制作的_r2_q1_4": {
+      "type": "sug",
+      "query": "[SUG] 小红书表情包怎么制作的",
+      "level": 23,
+      "relevance_score": 0.0049999999999999906,
+      "evaluationReason": "【评估对象】词条\"小红书表情包怎么制作的\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.20】原始问题意图是「制作」表情包梗图,sug词条意图是「制作」小红书表情包。两者都包含制作动作,但在制作对象上有差异,原始问题侧重意图表达,sug词条侧重平台属性,相关性较弱。\n【品类维度 -0.20】原始问题核心对象是《猫咪双标行为表情包梗图》,限定词有《人类双标行为》、《猫咪》。sug词条核心对象是《小红书表情包》,限定词是《小红书》。两者核心主体部分重合,但限定词完全不同,且《小红书》的限定范围无法涵盖《猫咪双标行为》。\n【延伸词维度 -0.15】延伸词“小红书”和“怎么制作的”与原始问题“反映人类双标行为的猫咪表情包梗图”的核心内容和目的不符。“小红书”引入了平台限定,稀释了原始问题对内容创作本身的关注;“怎么制作的”是通用制作方法,与原始问题中“反映人类双标行为的猫咪表情包梗图”的特定主题和创意需求无关,降低了内容的针对性。\n【最终得分 0.00】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.695
+    },
+    "sug_原创表情包怎么制作_r2_q1_5": {
+      "type": "sug",
+      "query": "[SUG] 原创表情包怎么制作",
+      "level": 23,
+      "relevance_score": 0.46699999999999997,
+      "evaluationReason": "【评估对象】词条\"原创表情包怎么制作\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.90】原始问题的核心动机是\"制作表情包梗图\",sug词条的动机是\"制作表情包\"。sug词条的动作\"制作\"与原始问题完全一致,属于高度匹配。\n【品类维度 0.08】原始问题对象层为「人类双标行为的猫咪表情包梗图」,场景层无。sug词条对象层为「原创表情包」。sug词条仅泛化匹配了部分对象(表情包),限定词「人类双标行为的猫咪」、「梗图」以及「原创」均未覆盖,覆盖度低,属于过度泛化。\n【延伸词维度 -0.15】sug词条「原创表情包怎么制作」中的“原创”和“怎么制作”属于原始问题作用域内的词汇,但“表情包”这一对象层面的词汇,与原始问题中的“反映人类双标行为的猫咪表情包梗图”相比,过于宽泛,稀释了原始问题中对“猫咪”和“双标行为”的特定要求,降低了内容的针对性。\n【最终得分 0.47】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.695
+    },
+    "sug_表情包怎么制作视频_r2_q1_6": {
+      "type": "sug",
+      "query": "[SUG] 表情包怎么制作视频",
+      "level": 23,
+      "relevance_score": 0.07999999999999997,
+      "evaluationReason": "【评估对象】词条\"表情包怎么制作视频\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.35】原始问题核心动机是「制作」表情包梗图,sug词条核心动机是「制作」视频。两者动作都是「制作」,但对象不同,一个针对表情包梗图,一个针对视频,属于弱相关。\n【品类维度 -0.20】原始问题需求的核心对象是「人类双标行为的猫咪表情包梗图」,涉及主题「双标行为」和主体「猫咪」,而sug词条仅提及「表情包」,且关注点是「制作视频」,与原始问题的主要对象、主题和形式均不匹配,存在误导性。\n【延伸词维度 -0.15】原始问题聚焦于「表情包梗图」的制作,而sug词条引入了「视频」这一延伸词。视频制作与梗图制作是两种不同的媒介和技能,稀释了原始问题对「梗图」的聚焦,降低了内容的针对性。\n【最终得分 0.08】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.695
+    },
+    "sug_表情包怎么制作动态图_r2_q1_7": {
+      "type": "sug",
+      "query": "[SUG] 表情包怎么制作动态图",
+      "level": 23,
+      "relevance_score": 0.21999999999999997,
+      "evaluationReason": "【评估对象】词条\"表情包怎么制作动态图\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.35】原始问题的核心动机是「制作」表情包梗图。sug词条的动机也是「制作」表情包相关内容,但其侧重于「动态图」的制作,而非原始问题中「反映人类双标行为的猫咪表情包梗图」的特定内容制作,因此关联度较弱。\n【品类维度 0.15】原始问题对象层为「猫咪表情包梗图」,场景层为「人类双标行为」。sug词条对象层为「表情包」,场景层为「动态图」,两者对象层有部分重叠,但限定词偏差较大,且sug词条缺少猫咪的限定。\n【延伸词维度 -0.15】原始问题聚焦于「猫咪表情包梗图」的「制作」和「反映双标行为」这一特定主题。sug词条「动态图」是制作方式的延伸,但原始问题并未提及对动态图的需求,且「动态图」的制作方式与「梗图」的制作方式可能存在差异,稀释了原始问题的核心目的和聚焦度。\n【最终得分 0.22】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.695
+    },
+    "sug_表情包怎么制作动图_r2_q1_8": {
+      "type": "sug",
+      "query": "[SUG] 表情包怎么制作动图",
+      "level": 23,
+      "relevance_score": 0.5599999999999999,
+      "evaluationReason": "【评估对象】词条\"表情包怎么制作动图\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.95】原始问题的核心动机是“制作”表情包梗图,Sug词条的核心动机是“制作”动图。动作上制作和制作完全匹配,仅对象存在差异,因此匹配度很高。\n【品类维度 0.25】原始问题对象层为「猫咪表情包梗图」,场景层为「反映人类双标行为」。Sug词条对象层为「表情包」,场景层为「动图」。核心对象「表情包」匹配,但sug词缺失关键限定词「猫咪」、「梗图」,并有不匹配的限定词「动图」。\n【延伸词维度 -0.15】原始问题聚焦于“猫咪表情包梗图”这一特定内容,而延伸词“动图”引入了新的形式要求,稀释了对内容本身的关注,且与原始问题中的“梗图”概念不完全匹配,可能导致用户偏离创作核心。\n【最终得分 0.56】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.695
+    },
+    "sug_制作表情包怎么赚钱_r2_q1_9": {
+      "type": "sug",
+      "query": "[SUG] 制作表情包怎么赚钱",
+      "level": 23,
+      "relevance_score": -0.17000000000000004,
+      "evaluationReason": "【评估对象】词条\"制作表情包怎么赚钱\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 -0.15】原始问题的核心动机是“制作”表情包,sug词条的动机是“赚钱”。两者都包含了“表情包”这个对象,但原始问题关注制作,sug词条关注赚钱,动机方向不同,属于轻度偏离。\n【品类维度 -0.20】原始问题核心对象是“猫咪表情包梗图”,限定词是“人类双标行为”;sug词条核心对象是“表情包”,限定词是“赚钱”。sug词条缺少原始核心对象“猫咪”和“梗图”,且限定词也完全不匹配,存在误导性。\n【延伸词维度 -0.15】sug词条「怎么赚钱」引入了原始问题未提及的商业化目的,稀释了原始问题「制作表情包」的创作和表达意图,属于作用域稀释型延伸词。\n【最终得分 -0.17】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.695
+    },
+    "q_双标表情包_r2_2": {
+      "type": "q",
+      "query": "[Q] 双标表情包",
+      "level": 22,
+      "relevance_score": 0.4,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": -1,
+      "domain_type": ""
+    },
+    "sug_双标表情包内涵_r2_q2_0": {
+      "type": "sug",
+      "query": "[SUG] 双标表情包内涵",
+      "level": 23,
+      "relevance_score": 0.32000000000000006,
+      "evaluationReason": "【评估对象】词条\"双标表情包内涵\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题明确意图是\"制作\"表情包,而sug词条\"双标表情包内涵\"聚焦于\"内涵\"这一主题,不包含制作这一动作意图,因此动机不匹配。\n【品类维度 0.40】原始问题对象层为「人类双标行为的猫咪表情包梗图」,场景层为「制作反映」。sug词条仅包含部分对象层「双标表情包」,核心主体和限定词有匹配但缺失「猫咪」限定和「梗图」限定,覆盖度较低。\n【延伸词维度 0.00】sug词条未引入延伸词,所有词汇均属于原始问题作用域范围。原始问题中的“双标”和“表情包”在sug词条中被直接使用,而“内涵”可以视为对“反映”这一动机的细化或同义表达。\n【最终得分 0.32】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.4
+    },
+    "sug_双标表情包图片_r2_q2_1": {
+      "type": "sug",
+      "query": "[SUG] 双标表情包图片",
+      "level": 23,
+      "relevance_score": 0.48,
+      "evaluationReason": "【评估对象】词条\"双标表情包图片\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题的核心动机是\"制作\",而sug词条\"双标表情包图片\"是一个名词短语,没有包含任何动作意图,因此sug词条在此维度上缺失动作意图。\n【品类维度 0.60】sug词条「双标表情包图片」包含了原始问题中的核心对象「表情包」和限定词「双标」。缺失了对象「猫咪」和「梗图」。主体部分匹配,但限定词不完全。\n【延伸词维度 0.00】sug词条未引入延伸词,所有词汇均属于原始问题作用域范围。原始问题中的“人类双标行为”被简化为“双标”,但核心概念未变;“猫咪表情包梗图”被简化为“表情包图片”,属于细化或同义词,不构成延伸。\n【最终得分 0.48】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.4
+    },
+    "sug_怼双标的人表情包_r2_q2_2": {
+      "type": "sug",
+      "query": "[SUG] 怼双标的人表情包",
+      "level": 23,
+      "relevance_score": 0.655,
+      "evaluationReason": "【评估对象】词条\"怼双标的人表情包\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.90】原始问题重点在“制作”表情包梗图,sug词条虽然是直接给出表情包,但不排除用户的制作意图,可以理解为寻找制作灵感或素材,因此动机一致。\n【品类维度 0.55】原始问题对象层为「猫咪表情包梗图」,场景层为「双标行为」。sug词条对象层为「表情包」,场景层为「怼双标的人」。对象层部分匹配(都为表情包),场景层部分匹配(都与双标相关)。内容主体有重叠。\n【延伸词维度 -0.15】原始问题聚焦于「制作」猫咪表情包梗图,且主题是「人类双标行为」。sug词条「怼双标的人表情包」将「制作」这一核心动机替换为「怼」,且未提及「猫咪」,稀释了原始问题的核心目的和对象。\n【最终得分 0.66】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.4
+    },
+    "sug_可爱又双标的表情包_r2_q2_3": {
+      "type": "sug",
+      "query": "[SUG] 可爱又双标的表情包",
+      "level": 23,
+      "relevance_score": 0.68,
+      "evaluationReason": "【评估对象】词条\"可爱又双标的表情包\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图),通过表情包梗图反映人类双标行为,主体是猫咪\n【动机维度 0.00】原始问题的核心动机是「制作」表情包梗图。sug词条「可爱又双标的表情包」没有包含任何动作意图,因此动机匹配度为0。\n【品类维度 0.85】原始问题对象层为「猫咪表情包梗图」,场景层为「人类双标行为」。sug词条对象层为「表情包」,场景层为「可爱又双标」。虽然未提及「猫咪」和「梗图」,但「双标」和「表情包」核心主体匹配,故属于部分场景层(双标)的匹配。\n【延伸词维度 0.00】sug词条未引入延伸词,所有词汇均属于原始问题作用域范围。原始问题中的“猫咪”被“可爱”所概括,而“梗图”被“表情包”所概括,均属于同义词或细化词。\n【最终得分 0.68】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.4
+    },
+    "sug_讽刺双标的图片_r2_q2_4": {
+      "type": "sug",
+      "query": "[SUG] 讽刺双标的图片",
+      "level": 23,
+      "relevance_score": 0.49,
+      "evaluationReason": "【评估对象】词条\"讽刺双标的图片\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(猫咪表情包梗图),这个表情包梗图需用于反映/讽刺人类双标行为,因此‘制作’是核心动机,‘反映/讽刺’是目的。\n【动机维度 0.00】原始问题意图是“制作”表情包梗图,而sug词条「讽刺双标的图片」是名词描述,无明确动作意图。sug词条未包含原始问题的核心动机。\n【品类维度 0.65】原始问题核心对象为「猫咪表情包梗图」,限定词为「反映人类双标行为」。sug词核心对象为「图片」,限定词为「讽刺双标」。其中「图片」是「梗图」的泛化,且「讽刺双标」与「反映人类双标行为」高度匹配,但缺失核心对象「猫咪」的限定。覆盖度较高,但有一定泛化和缺失,故给0.65分。\n【延伸词维度 -0.15】原始问题聚焦于「猫咪表情包梗图」这一特定形式,而sug词条「图片」泛化了对象,稀释了原始问题的具体性和趣味性,属于作用域稀释型。\n【最终得分 0.49】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.4
+    },
+    "sug_双标梗图模板_r2_q2_5": {
+      "type": "sug",
+      "query": "[SUG] 双标梗图模板",
+      "level": 23,
+      "relevance_score": 0.29000000000000004,
+      "evaluationReason": "【评估对象】词条\"双标梗图模板\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作梗图\n【动机维度 0.00】原始问题是「制作」反映人类双标行为的猫咪表情包梗图,sug词条是「双标梗图模板」。sug词条没有明确的动作,可以理解为学习、查看或获取模板,与原始问题的「制作」动作方向存在差异,因此动作方向上不匹配。考虑到sug词条本身无明确动机,得分为0。\n【品类维度 0.40】原始问题对象层为「猫咪表情包梗图」,场景层为「人类双标行为」。sug词条对象层为「梗图模板」,是原始问题对象层「梗图」的泛化且增加了「模板」限定,场景层为「双标」。主体匹配但限定有所泛化。\n【延伸词维度 -0.15】原始问题聚焦于「猫咪表情包」这一特定对象,而sug词条「模板」将范围扩大到所有「双标梗图」,稀释了原始问题的核心对象,降低了内容的针对性。\n【最终得分 0.29】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.4
+    },
+    "sug_讽刺双标的人的文案_r2_q2_6": {
+      "type": "sug",
+      "query": "[SUG] 讽刺双标的人的文案",
+      "level": 23,
+      "relevance_score": -0.14,
+      "evaluationReason": "【评估对象】词条\"讽刺双标的人的文案\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 -0.05】原始问题的核心动机是「制作」表情包梗图。sug词条的动机是「讽刺双标的人的文案」,这是一种「创作」行为或「寻找」行为,与原始问题的「制作」方向有轻微偏离。原始问题侧重行为主体,sug词条侧重内容主题。\n【品类维度 -0.25】原始问题核心对象是「猫咪表情包梗图」,场景限定为「人类双标行为」。sug词条核心对象为「文案」,场景限定为「讽刺双标的人」。对象错位,且场景限定差异较大。\n【延伸词维度 -0.15】原始问题聚焦于「制作猫咪表情包梗图」这一具体行为和对象,延伸词「文案」和「讽刺双标的人」偏离了制作表情包的动作和猫咪这一核心对象,稀释了原始问题的聚焦度。\n【最终得分 -0.14】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.4
+    },
+    "sug_很双标的朋友圈文案_r2_q2_7": {
+      "type": "sug",
+      "query": "[SUG] 很双标的朋友圈文案",
+      "level": 23,
+      "relevance_score": -0.19000000000000003,
+      "evaluationReason": "【评估对象】词条\"很双标的朋友圈文案\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题核心动机是“制作”,sug词条“朋友圈文案”无明确动作意图,因此动机维度得分设为0。\n【品类维度 -0.20】原始问题核心是《猫咪表情包梗图》,限定词有《人类双标行为》。sug词条核心是《朋友圈文案》,限定词是《很双标》。sug与原始问题的主体“猫咪表情包梗图”完全不匹配,但限定词《双标》部分匹配,因此给予负分。\n【延伸词维度 -0.15】sug词条「朋友圈文案」与原始问题「猫咪表情包梗图」在内容形式上完全不符,且「朋友」与「人类」的范围差异,稀释了原始问题的聚焦度,导致内容偏离。\n【最终得分 -0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.4
+    },
+    "sug_这可是要砍头的表情包_r2_q2_8": {
+      "type": "sug",
+      "query": "[SUG] 这可是要砍头的表情包",
+      "level": 23,
+      "relevance_score": -0.19000000000000003,
+      "evaluationReason": "【评估对象】词条\"这可是要砍头的表情包\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作/生成\n【动机维度 0.00】原始问题的核心动机是「制作/生成」,sug词条「这可是要砍头的表情包」仅是一句描述性短语,没有体现任何动作或意图。\n【品类维度 -0.20】原始问题核心是关于《人类双标行为》、《猫咪表情包》以及《梗图》的主体内容。平台sug词条仅包含《表情包》,且带有负面情绪限定,主体高度不匹配。\n【延伸词维度 -0.15】sug词条「这可是要砍头的表情包」与原始问题「如何制作反映人类双标行为的猫咪表情包梗图」的核心目的和作用域完全不符,引入了无关的、具有误导性的信息,稀释了原始问题的聚焦度,属于作用域稀释型。\n【最终得分 -0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.4
+    },
+    "sug_双标的人怎么怼_r2_q2_9": {
+      "type": "sug",
+      "query": "[SUG] 双标的人怎么怼",
+      "level": 23,
+      "relevance_score": -0.17500000000000004,
+      "evaluationReason": "【评估对象】词条\"双标的人怎么怼\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题意图是「制作」表情包梗图,而sug词条「怎么怼」意图是「反击/对抗」。两者动作意图完全不匹配。\n【品类维度 -0.40】原始问题内容主体为「人类双标行为」和「猫咪表情包梗图」,sug词条内容主体为「双标的人」。sug只有「双标」相关概念,缺少「猫咪表情包梗图」这一核心对象,且sug词侧重于互动反驳,与原始问题制作内容的主体含义有较大差异,存在品类错位。\n【延伸词维度 -0.15】sug词条「双标的人怎么怼」中的“怼”是延伸词,它引入了与原始问题“制作表情包梗图”完全不相关的行为,稀释了原始问题的创作和表达目的,偏离了核心需求。\n【最终得分 -0.18】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.4
+    },
+    "q_梗图素材_r2_3": {
+      "type": "q",
+      "query": "[Q] 梗图素材",
+      "level": 22,
+      "relevance_score": 0.21000000000000002,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": -1,
+      "domain_type": ""
+    },
+    "sug_梗图素材模板_r2_q3_0": {
+      "type": "sug",
+      "query": "[SUG] 梗图素材模板",
+      "level": 23,
+      "relevance_score": 0.084,
+      "evaluationReason": "【评估对象】词条\"梗图素材模板\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题的核心动机是\"制作\",而sug词条\"梗图素材模板\"不包含任何动作意图,因此动机维度评分为0。\n【品类维度 0.08】原始问题涉及“猫咪表情包梗图”这一特定对象,sug词条“梗图素材模板”仅包含泛化的“梗图”对象,且限定词几乎为零,覆盖度极低。\n【延伸词维度 0.10】原始问题是制作梗图,sug词条中的“素材模板”是制作梗图的辅助工具,对原始问题有辅助作用,但非必需,属于作用域辅助型延伸词。\n【最终得分 0.08】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.21000000000000002
+    },
+    "sug_梗图素材抽象_r2_q3_1": {
+      "type": "sug",
+      "query": "[SUG] 梗图素材抽象",
+      "level": 23,
+      "relevance_score": 0.41000000000000003,
+      "evaluationReason": "【评估对象】词条\"梗图素材抽象\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.00】原始问题的核心动机是\"制作\"梗图,而sug词条只提供了\"梗图素材\",没有明确的动作意图。因此,sug词条缺失了动机层。\n【品类维度 0.55】原始问题核心对象是「猫咪表情包梗图」,限定词「反映人类双标行为」。sug词条核心对象「梗图素材」,限定词「抽象」。sug词条对象「梗图素材」与原始问题「猫咪表情包梗图」的主对象「梗图」匹配,但对象上缺失「猫咪表情包」限定词,限定词差异较大,故评为中等偏上匹配。\n【延伸词维度 -0.15】原始问题聚焦于「制作反映人类双标行为的猫咪表情包梗图」,强调了主题、对象和行为。sug词条「梗图素材抽象」中的「素材」和「抽象」是延伸词。「素材」属于作用域辅助型,对制作有辅助作用,但「抽象」与原始问题明确的「双标行为」主题相悖,稀释了原始问题的聚焦度,降低了内容的针对性,属于作用域稀释型。\n【最终得分 0.41】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.21000000000000002
+    },
+    "sug_梗图素材双人_r2_q3_2": {
+      "type": "sug",
+      "query": "[SUG] 梗图素材双人",
+      "level": 23,
+      "relevance_score": 0.010000000000000009,
+      "evaluationReason": "【评估对象】词条\"梗图素材双人\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包、梗图)\n【动机维度 0.00】sug词条「梗图素材双人」缺乏明确的动作意图。原始问题的核心动作是「制作」,sug词条虽然提及「梗图」,但无明确制作动作,因此无法评估动机匹配度。\n【品类维度 0.05】原始问题核心对象为「猫咪表情包梗图」,限定词为「反映人类双标行为」。sug词条「梗图素材双人」仅包含「梗图」这一核心对象的部分特征。内容主体匹配度极低,仅能弱关联。\n【延伸词维度 -0.15】原始问题聚焦于「猫咪表情包梗图」的制作,而sug词条「双人」引入了与原始问题对象不符的元素,稀释了对「猫咪」这一核心对象的关注,降低了内容的针对性。\n【最终得分 0.01】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.21000000000000002
+    },
+    "sug_梗图素材单人_r2_q3_3": {
+      "type": "sug",
+      "query": "[SUG] 梗图素材单人",
+      "level": 23,
+      "relevance_score": 0.034,
+      "evaluationReason": "【评估对象】词条\"梗图素材单人\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是“制作”表情包梗图。sug词条“梗图素材单人”仅提供了制作梗图所需的部分对象(素材),但没有包含任何动机层信息,无法评估动机匹配度。\n【品类维度 0.08】原始问题涉及“猫咪表情包梗图”和“双标行为”两个核心对象,sug词条仅包含“梗图”这一通用对象,且加入“单人”这一与原问题中“猫咪”和“双标行为”不符的限定,对象覆盖度极低并存在语义错位,为过度泛化。\n【延伸词维度 -0.15】原始问题聚焦于「制作」反映「人类双标行为」的「猫咪表情包梗图」,强调主题和内容。sug词条「素材」是制作梗图的辅助,但「单人」作为限定词,与原始问题中的「猫咪表情包」和「人类双标行为」的主题不符,稀释了原始问题的核心内容,降低了聚焦度。\n【最终得分 0.03】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.21000000000000002
+    },
+    "sug_梗图素材原图_r2_q3_4": {
+      "type": "sug",
+      "query": "[SUG] 梗图素材原图",
+      "level": 23,
+      "relevance_score": 0.034,
+      "evaluationReason": "【评估对象】词条\"梗图素材原图\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作/反映\n【动机维度 0.00】原始问题的核心动机是“制作”和“反映”梗图,而sug词条“梗图素材原图”没有明确的动作意图,仅为名词短语,因此sug词条没有动机。\n【品类维度 0.08】原始问题的核心对象为「猫咪表情包梗图」,限定词为「反映人类双标行为」。sug词条「梗图素材原图」是通用对象「梗图」的泛化概念。sug词只体现了对象层面的泛化,未包含任何重要限定词,覆盖度低。\n【延伸词维度 -0.15】原始问题聚焦于「制作」反映「人类双标行为」的「猫咪表情包梗图」这一具体创意过程。「梗图素材原图」中的「素材原图」是延伸词,它将原始问题从创意制作过程稀释为单纯的素材获取,降低了对核心创意和主题的关注度,属于作用域稀释型。\n【最终得分 0.03】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.21000000000000002
+    },
+    "sug_梗图素材多人_r2_q3_5": {
+      "type": "sug",
+      "query": "[SUG] 梗图素材多人",
+      "level": 23,
+      "relevance_score": -0.19000000000000003,
+      "evaluationReason": "【评估对象】词条\"梗图素材多人\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包/梗图\n【动机维度 0.00】原始问题的核心动机是「制作」梗图,而sug词条「梗图素材多人」缺少明确的动作意图。sug词条提供了制作梗图的其中一个要素「素材」,但未体现「制作」这一核心动作,因此动机维度评分为0。\n【品类维度 -0.20】原始问题核心对象为「猫咪表情包梗图」,限定词为「人类双标行为」。sug词条核心对象为「梗图素材」,限定词「多人」。对象层部分匹配,但「多人」与「猫咪」限定词差异大且不相关,且缺乏「表情包」限定词,存在误导性。\n【延伸词维度 -0.15】原始问题聚焦于「猫咪表情包梗图」的制作,而延伸词「素材」虽然与制作相关,但「多人」限定词与原始问题中的「猫咪」对象不符,稀释了原始问题的核心对象,导致聚焦度下降。\n【最终得分 -0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.21000000000000002
+    },
+    "sug_梗图素材画画_r2_q3_6": {
+      "type": "sug",
+      "query": "[SUG] 梗图素材画画",
+      "level": 23,
+      "relevance_score": -0.23,
+      "evaluationReason": "【评估对象】词条\"梗图素材画画\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.00】原始问题的核心动机是「制作」反映人类双标行为的猫咪表情包梗图。sug词条「梗图素材画画」虽然包含「梗图」和「画画」可能指代制作过程,但没有明确表达制作的动作意图。\n【品类维度 -0.25】原始问题对象层为「猫咪表情包梗图」,场景层为「反映人类双标行为」。Sug词条对象层为「梗图素材」「画画」,限定词缺失。两者主体类别差异大,且sug词过于泛化,关联性较低。\n【延伸词维度 -0.15】原始问题聚焦于「制作」特定主题的「猫咪表情包梗图」,而sug词条「素材画画」引入了与「制作」方式不符的「画画」这一延伸词,稀释了原始问题中「表情包梗图」的制作方式,降低了内容的针对性。\n【最终得分 -0.23】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.21000000000000002
+    },
+    "sug_梗图素材描改_r2_q3_7": {
+      "type": "sug",
+      "query": "[SUG] 梗图素材描改",
+      "level": 23,
+      "relevance_score": 0.07999999999999997,
+      "evaluationReason": "【评估对象】词条\"梗图素材描改\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包/梗图\n【动机维度 0.35】原始问题的核心动机是「制作」反映人类双标行为的猫咪表情包梗图。sug词条「梗图素材描改」包含了「梗图」和「描改」动作,描改是制作的子集动作,动机方向相关。\n【品类维度 -0.20】原始问题核心对象为「猫咪表情包梗图」,限定词为「人类双标行为」;sug词是「梗图素材描改」,对象层面缺少「猫咪表情包」,且限定词均未覆盖。sug词仅能体现梗图的制作方式,而非内容主题。\n【延伸词维度 -0.15】原始问题聚焦于「制作」特定主题的表情包梗图,强调「人类双标行为」和「猫咪」元素。「素材描改」作为延伸词,虽然与梗图制作相关,但它将原始问题从「制作」具体内容的层面,稀释到了「素材」和「描改」这种更基础、更宽泛的制作方法上,偏离了原始问题对内容和主题的聚焦,降低了针对性。\n【最终得分 0.08】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.21000000000000002
+    },
+    "sug_梗图素材表情包_r2_q3_8": {
+      "type": "sug",
+      "query": "[SUG] 梗图素材表情包",
+      "level": 23,
+      "relevance_score": 0.4,
+      "evaluationReason": "【评估对象】词条\"梗图素材表情包\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题意图是“制作”表情包梗图,而sug词条「梗图素材表情包」没有明确的动作意图。\n【品类维度 0.50】原始问题的核心对象是“猫咪表情包梗图”,限定词为“反映人类双标行为”。sug词条“梗图素材表情包”包含了核心对象“梗图”和“表情包”,但缺失了“猫咪”这一关键限定词,且完全缺失了“反映人类双标行为”这一具体限定,故匹配度一般。\n【延伸词维度 0.00】sug词条未引入延伸词,所有词汇均属于原始问题作用域范围。原始问题中的“表情包梗图”与sug词条中的“梗图素材表情包”在概念上是高度重合的,sug词条仅是对原始问题中对象层的重组和细化,不构成延伸。\n【最终得分 0.40】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.21000000000000002
+    },
+    "sug_梗图素材聊天记录_r2_q3_9": {
+      "type": "sug",
+      "query": "[SUG] 梗图素材聊天记录",
+      "level": 23,
+      "relevance_score": 0.010000000000000009,
+      "evaluationReason": "【评估对象】词条\"梗图素材聊天记录\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.00】原始问题的核心动机是“制作表情包梗图”。sug词条「梗图素材聊天记录」缺失了动作意图,只提供了制作梗图的其中一种材料“聊天记录”,无法识别动作,故动机维度得分为0。\n【品类维度 0.05】原始问题对象层为「猫咪表情包梗图」,场景层为「反映人类双标行为」。Sug词条对象层为「梗图素材」、「聊天记录」。Sug词条内容主体过度泛化,与原始问题的特定主体不匹配。\n【延伸词维度 -0.15】原始问题聚焦于「制作」特定主题的「猫咪表情包梗图」,强调创作过程和内容。sug词条「素材聊天记录」引入了「聊天记录」这一新的素材类型,与原始问题中「猫咪表情包」的核心对象不符,稀释了主题的聚焦度,降低了内容针对性。\n【最终得分 0.01】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.21000000000000002
+    },
+    "q_梗图模版_r2_4": {
+      "type": "q",
+      "query": "[Q] 梗图模版",
+      "level": 22,
+      "relevance_score": 0.19400000000000003,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": -1,
+      "domain_type": ""
+    },
+    "sug_梗图模版抽象_r2_q4_0": {
+      "type": "sug",
+      "query": "[SUG] 梗图模版抽象",
+      "level": 23,
+      "relevance_score": 0.034,
+      "evaluationReason": "【评估对象】词条\"梗图模版抽象\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题意图是“制作”梗图,而sug词条“梗图模版抽象”中没有明确的动作意图。\n【品类维度 0.08】原始问题是关于“猫咪表情包梗图”这一特定概念,sug词条“梗图模板抽象”是泛化的通用概念,仅包含核心对象“梗图”的抽象分类,缺失限定词和具体主体。因此匹配度低。\n【延伸词维度 -0.15】原始问题聚焦于「制作反映人类双标行为的猫咪表情包梗图」这一具体内容创作,而sug词条「梗图模版抽象」引入了「模版」和「抽象」两个延伸词。这两个词与原始问题中的具体创作目标关联度较低,稀释了原始问题的聚焦度,降低了内容针对性。\n【最终得分 0.03】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.19400000000000003
+    },
+    "sug_梗图模版双人_r2_q4_1": {
+      "type": "sug",
+      "query": "[SUG] 梗图模版双人",
+      "level": 23,
+      "relevance_score": 0.010000000000000009,
+      "evaluationReason": "【评估对象】词条\"梗图模版双人\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题核心动机是「制作」反映人类双标行为的猫咪表情包梗图,sug词条「梗图模版双人」无明确的动作意图。\n【品类维度 0.05】原始问题涉及“猫咪表情包梗图”,sug词为“梗图模版双人”。sug词保留了不限定主体的“梗图”这一通用对象层,但丢失了核心对象《猫咪》和限定词《表情包》《双标行为》等,未明确体现《猫咪》主题,且增加了《双人》泛化限定词,造成语义错位。\n【延伸词维度 -0.15】原始问题聚焦于「猫咪表情包梗图」的「制作」和「反映人类双标行为」这一主题。sug词条「梗图模版双人」中的「双人」是延伸词,它限定了梗图模版的人物数量,与原始问题中「猫咪表情包」和「人类双标行为」的主题不符,稀释了原始问题的聚焦度,降低了内容的针对性。\n【最终得分 0.01】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.19400000000000003
+    },
+    "sug_梗图模版单人_r2_q4_2": {
+      "type": "sug",
+      "query": "[SUG] 梗图模版单人",
+      "level": 23,
+      "relevance_score": -0.23,
+      "evaluationReason": "【评估对象】词条\"梗图模版单人\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题的核心动机是「制作」反映特定主题的猫咪表情包梗图,sug词条「梗图模版单人」仅提及「梗图模版」,无法识别任何动作意图,因此动机维度得分设为0。\n【品类维度 -0.25】原始问题对象层为「猫咪表情包梗图」,场景层为「人类双标行为」。sug词条对象层为「梗图模版」,场景层为「单人」。sug词仅有部分对象层匹配,无场景层匹配,且模版与特定主题梗图品类差距较大,有偏离性,故评为中低分。\n【延伸词维度 -0.15】原始问题聚焦于「猫咪表情包梗图」的「制作」和「反映双标行为」这一特定主题。sug词条「梗图模版单人」中的「单人」是延伸词,它限制了梗图模版的使用场景,与原始问题中未限定的「猫咪表情包梗图」存在冲突,稀释了原始问题的聚焦度,降低了内容的针对性。\n【最终得分 -0.23】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.19400000000000003
+    },
+    "sug_梗图模板_r2_q4_3": {
+      "type": "sug",
+      "query": "[SUG] 梗图模板",
+      "level": 23,
+      "relevance_score": 0.034,
+      "evaluationReason": "【评估对象】词条\"梗图模板\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】原始问题核心动机是“制作”表情包梗图。\n【动机维度 0.00】原始问题的核心动机是“制作”,sug词条“梗图模板”没有明确的动作意图,不涉及制作行为,因此动机维度评分为0。\n【品类维度 0.08】原始问题是关于《人类双标行为的猫咪表情包梗图》的具体制作,sug词条《梗图模板》过于泛化,只涵盖了梗图的通用概念,未提及猫咪、双标行为等核心要素,覆盖度低。\n【延伸词维度 -0.15】原始问题聚焦于「制作反映人类双标行为的猫咪表情包梗图」这一具体创意内容,而延伸词「模板」则将范围扩大到所有梗图,稀释了原始问题的核心创意和特定主题,降低了内容的针对性。\n【最终得分 0.03】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.19400000000000003
+    },
+    "sug_梗图模版三人_r2_q4_4": {
+      "type": "sug",
+      "query": "[SUG] 梗图模版三人",
+      "level": 23,
+      "relevance_score": -0.19000000000000003,
+      "evaluationReason": "【评估对象】词条\"梗图模版三人\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作/生成(梗图)\n【动机维度 0.00】原始问题的核心动机是「制作」反映人类双标行为的猫咪表情包梗图。sug词条「梗图模版三人」没有明确的动作意图。因此,动机维度不匹配。\n【品类维度 -0.20】原始问题核心对象是“猫咪表情包梗图”,限定词是“反映人类双标行为”;sug词条是“梗图模板三人”。sug词条只涵盖了“梗图”这一通用对象,且其限定词“三人”与原始问题的核心对象和限定词完全不符,存在品类错位。\n【延伸词维度 -0.15】原始问题聚焦于「制作反映人类双标行为的猫咪表情包梗图」这一特定主题和内容,而sug词条「梗图模版三人」中的「三人」是延伸词,它引入了与原始问题核心内容无关的限定条件,稀释了原始问题的聚焦度,降低了内容针对性。\n【最终得分 -0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.19400000000000003
+    },
+    "sug_梗图模版表情包_r2_q4_5": {
+      "type": "sug",
+      "query": "[SUG] 梗图模版表情包",
+      "level": 23,
+      "relevance_score": 0.33000000000000007,
+      "evaluationReason": "【评估对象】词条\"梗图模版表情包\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图),通过描绘猫咪来反映人类双标行为,其核心意图是「制作」并传达「人类双标行为」\n【动机维度 0.00】原始问题核心动机是「制作」反映特定主题的梗图。sug词条「梗图模版表情包」本身是一个名词短语,不包含任何动作意图,因此无法与原始问题的动作意图匹配。\n【品类维度 0.45】原始问题对象层为“猫咪表情包梗图”,限定词为“反映人类双标行为”;sug词条对象层为“梗图模版表情包”,与原始问题主体部分匹配,但限定词全部缺失。\n【延伸词维度 -0.15】原始问题聚焦于「制作」特定主题(人类双标行为的猫咪表情包梗图),强调创意和内容。sug词条「梗图模版表情包」引入了「模版」这一延伸词,稀释了原始问题中「制作」的创意性和个性化需求,可能导致用户偏离原创制作,转而寻找现成模版,降低了内容的针对性。\n【最终得分 0.33】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.19400000000000003
+    },
+    "sug_梗图模版多人_r2_q4_6": {
+      "type": "sug",
+      "query": "[SUG] 梗图模版多人",
+      "level": 23,
+      "relevance_score": -0.19000000000000003,
+      "evaluationReason": "【评估对象】词条\"梗图模版多人\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是“制作”梗图,而sug词条“梗图模版多人”没有明确的动作,无法评估动机匹配度。\n【品类维度 -0.20】原始问题核心对象是“猫咪表情包梗图”,限定词为“反映人类双标行为”。Sug词条核心对象为“梗图模版”,限定词为“多人”。核心对象存在部分匹配(梗图),但限定词完全不匹配,且“模版”与“表情包”有差异,模版更具工具性。\n【延伸词维度 -0.15】原始问题聚焦于「猫咪表情包梗图」的制作方法和主题「人类双标行为」,而sug词条「梗图模版多人」引入了「模版」和「多人」这两个延伸词。这两个延伸词与原始问题的主题和制作对象(猫咪表情包)关联度低,且「多人」与「猫咪」主题相悖,稀释了原始问题的聚焦度,降低了内容针对性。\n【最终得分 -0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.19400000000000003
+    },
+    "sug_梗图模版四人_r2_q4_7": {
+      "type": "sug",
+      "query": "[SUG] 梗图模版四人",
+      "level": 23,
+      "relevance_score": -0.19000000000000003,
+      "evaluationReason": "【评估对象】词条\"梗图模版四人\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(猫咪表情包梗图,反映人类双标行为)\n【动机维度 0.00】原始问题的核心动机是\"制作\",sug词条\"梗图模版四人\"未包含任何显性或隐性的动作意图,无法评估动作匹配度。\n【品类维度 -0.20】原始问题是关于《猫咪表情包梗图》的制作,并限定了《人类双标行为》。sug词仅有《梗图》的主体,但限定词为《四人》,与原始问题《猫咪》、《人类双标行为》的内容主体完全不匹配,存在误导性。\n【延伸词维度 -0.15】sug词条「模版」和「四人」是延伸词。「模版」与原始问题中的「制作」有一定关联,但未明确指出是表情包模版,且「四人」与猫咪表情包的主题不符,稀释了原始问题的聚焦度。\n【最终得分 -0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.19400000000000003
+    },
+    "sug_双眼皮模版图_r2_q4_8": {
+      "type": "sug",
+      "query": "[SUG] 双眼皮模版图",
+      "level": 23,
+      "relevance_score": -0.6760000000000002,
+      "evaluationReason": "【评估对象】词条\"双眼皮模版图\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的动机是“制作”表情包梗图,而sug词条「双眼皮模版图」无明确动机,侧重于“图”这一客体。\n【品类维度 -0.80】原始问题核心对象为「猫咪表情包梗图」及「双标行为」,sug词条「双眼皮模版图」与原始问题对象层和场景层均无任何关联,两者完全不属于同一品类,为严重的负向偏离。\n【延伸词维度 -0.18】sug词条「双眼皮模版图」与原始问题「制作反映人类双标行为的猫咪表情包梗图」的核心目的和作用域完全不符,引入了无关信息,严重稀释了原始问题的聚焦度,属于作用域稀释型。\n【最终得分 -0.68】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.19400000000000003
+    },
+    "sug_梗图模版表格_r2_q4_9": {
+      "type": "sug",
+      "query": "[SUG] 梗图模版表格",
+      "level": 23,
+      "relevance_score": 0.07999999999999997,
+      "evaluationReason": "【评估对象】词条\"梗图模版表格\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.35】原始问题意图是「制作表情包梗图」,sug词条「梗图模版表格」提供了制作梗图的辅助工具或资源,属于实现原始意图的间接相关动作。虽然sug词条本身无明确动作,但作为制作梗图的辅助途径,有弱相关性。\n【品类维度 -0.20】原始问题核心对象是“猫咪表情包梗图”,sug词条是“梗图模板表格”。sug词条的“梗图”与核心对象部分匹配,但“模板”与“表格”限定词不符,且缺失核心实体“猫咪”,无法匹配原始问题主体形成有效匹配。\n【延伸词维度 -0.15】原始问题聚焦于「制作反映人类双标行为的猫咪表情包梗图」这一特定主题和内容,而sug词条「梗图模版表格」引入了与原始问题核心内容无关的「模版」和「表格」概念,稀释了原始问题的聚焦度,降低了内容针对性。\n【最终得分 0.08】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.19400000000000003
+    },
+    "q_梗图描改_r2_5": {
+      "type": "q",
+      "query": "[Q] 梗图描改",
+      "level": 22,
+      "relevance_score": 0.192,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": -1,
+      "domain_type": ""
+    },
+    "sug_梗图描改接稿_r2_q5_0": {
+      "type": "sug",
+      "query": "[SUG] 梗图描改接稿",
+      "level": 23,
+      "relevance_score": -0.16500000000000004,
+      "evaluationReason": "【评估对象】词条\"梗图描改接稿\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包/梗图\n【动机维度 -0.10】原始问题意图是「制作」梗图,sug词条「描改接稿」的「接稿」意在寻求制作服务,与用户「制作」意图方向略有偏差。\n【品类维度 -0.25】原始问题的核心对象是《人类双标行为的猫咪表情包梗图》,sug词的核心对象是《梗图描改接稿》,二者对象完全不匹配,品类冲突。\n【延伸词维度 -0.15】原始问题是关于「制作」特定主题的表情包梗图,而sug词条「描改接稿」引入了「描改」和「接稿」两个延伸词。这两个词与原始问题的「制作」行为不完全匹配,且「接稿」偏离了用户自主制作的意图,稀释了原始问题的聚焦度。\n【最终得分 -0.17】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.192
+    },
+    "sug_梗图描改素材_r2_q5_1": {
+      "type": "sug",
+      "query": "[SUG] 梗图描改素材",
+      "level": 23,
+      "relevance_score": 0.034,
+      "evaluationReason": "【评估对象】词条\"梗图描改素材\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题意图是「制作」表情包梗图。sug词条「梗图描改素材」主要指示获取素材,没有体现出「制作」的动作意图。\n【品类维度 0.08】原始问题是关于『猫咪表情包梗图』,sug词『梗图描改素材』,原始问题具有很强的特定性,sug词泛化,只存在概念上的父子关系,因此得分很低\n【延伸词维度 -0.15】原始问题聚焦于「制作反映人类双标行为的猫咪表情包梗图」这一具体创意和内容,而sug词条「描改素材」则将重点转移到素材的获取和修改上。这稀释了原始问题中关于「双标行为」、「猫咪表情包」等核心创意和主题的关注度,降低了内容的针对性。\n【最终得分 0.03】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.192
+    },
+    "sug_梗图描改教程_r2_q5_2": {
+      "type": "sug",
+      "query": "[SUG] 梗图描改教程",
+      "level": 23,
+      "relevance_score": 0.705,
+      "evaluationReason": "【评估对象】词条\"梗图描改教程\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(梗图)\n【动机维度 0.90】原始问题的核心动机是「制作」梗图。sug词条中的「描改」是制作梗图的一种具体方法或技巧,属于制作行为的一部分,动机高度相关。\n【品类维度 0.25】原始问题对象层为「猫咪表情包梗图」,场景层为「反映人类双标行为」。Sug词条仅提及「梗图」,对象层匹配度低,场景层缺失,覆盖度低。\n【延伸词维度 0.00】sug词条未引入延伸词,所有词汇均属于原始问题作用域范围。原始问题是关于制作梗图,sug词条的“梗图”和“教程”均属于原始问题的作用域。\n【最终得分 0.70】\n【规则说明】情况4:无延伸词,权重调整为 动机70% + 品类30%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.192
+    },
+    "sug_梗图描改怎么画_r2_q5_3": {
+      "type": "sug",
+      "query": "[SUG] 梗图描改怎么画",
+      "level": 23,
+      "relevance_score": 0.105,
+      "evaluationReason": "【评估对象】词条\"梗图描改怎么画\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.40】原始问题意图是“制作”猫咪表情包梗图,sug词条意图是“画(梗图)”,两者都指代制作梗图,是相关的行为。但sug词条中包含“描改”这一具体方法,只是制作的一种,因此是相关但在方法层面有差异。\n【品类维度 -0.20】原始问题的核心内容主体是「猫咪表情包梗图」,限定词是「人类双标行为」。Sug词条是「梗图描改」,对象层面通用,未提及核心内容主体的限定词,更侧重制作方法,品类差异较大。\n【延伸词维度 -0.15】原始问题聚焦于「制作猫咪表情包梗图」这一特定主题,并强调「反映人类双标行为」。sug词条「描改怎么画」引入了「描改」这一新的制作方式,且未提及猫咪或双标行为,稀释了原始问题的核心主题和目的,属于作用域稀释型。\n【最终得分 0.10】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.192
+    },
+    "sug_梗图描改模板_r2_q5_4": {
+      "type": "sug",
+      "query": "[SUG] 梗图描改模板",
+      "level": 23,
+      "relevance_score": 0.28500000000000003,
+      "evaluationReason": "【评估对象】词条\"梗图描改模板\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.40】原始问题意图是「制作」表情包梗图,而sug词条「梗图描改模板」虽然与「梗图」主题相关,但其核心动作是「描改模板」,与「制作」存在一定差异,描改是制作的辅助操作,所以相关联,但并非直接匹配。\n【品类维度 0.25】原始问题核心对象是《猫咪表情包梗图》,限定词是《人类双标行为》。sug词条《梗图描改模板》只包含了核心对象《梗图》,但完整度一般,没有包含任何限定词。\n【延伸词维度 -0.15】原始问题聚焦于「制作反映人类双标行为的猫咪表情包梗图」这一特定主题和内容。sug词条「描改模板」虽然与「制作」行为相关,但它将原始问题限定在「描改」这一具体方法上,且未提及「猫咪表情包」或「双标行为」等核心内容,稀释了原始问题的聚焦度,降低了内容针对性。\n【最终得分 0.29】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.192
+    },
+    "sug_梗图描改约稿_r2_q5_5": {
+      "type": "sug",
+      "query": "[SUG] 梗图描改约稿",
+      "level": 23,
+      "relevance_score": 0.0050000000000000044,
+      "evaluationReason": "【评估对象】词条\"梗图描改约稿\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是\"制作\"表情包梗图。sug词条「梗图描改约稿」中的「描改」和「约稿」是两种不同行为,描改可以理解为二次创作或制作的一种方式,但「约稿」是委托他人制作。sug词条未聚焦于\"制作\",且引入了新动作「约稿」,并非核心动作。\n【品类维度 0.05】sug词条只涵盖了与原始问题相关的“梗图”这一通用对象层元素,情感色彩和主要意图与原始问题不符。原始问题更具体,要求猫咪表情包和双标行为,而sug词条未提及,造成巨大的限定词义偏差。\n【延伸词维度 -0.15】原始问题是关于“制作”特定主题的表情包梗图,强调的是创作过程和内容。sug词条中的“描改”和“约稿”是与“制作”相关的行为,但“约稿”引入了商业或委托的维度,稀释了原始问题中可能包含的个人创作或学习制作的意图。同时,“描改”是制作梗图的一种方式,但并非唯一方式,且原始问题未限定制作方式。因此,这些延伸词对原始问题的作用域有轻微稀释作用。\n【最终得分 0.01】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.192
+    },
+    "sug_梗图描改原图_r2_q5_6": {
+      "type": "sug",
+      "query": "[SUG] 梗图描改原图",
+      "level": 23,
+      "relevance_score": 0.15499999999999997,
+      "evaluationReason": "【评估对象】词条\"梗图描改原图\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.50】原始问题意图是「制作」反映人类双标行为的猫咪表情包梗图,sug词条是「梗图描改原图」,「描改」是「制作」梗图的一种具体方法,所以动作意图相关。\n【品类维度 -0.20】原始问题核心对象是《猫咪表情包梗图》,限定词包括《人类双标行为》。sug词条核心对象为《梗图》,但限定词《描改原图》与原始问题对象的核心属性和限定词均不匹配,存在偏离。\n【延伸词维度 -0.15】原始问题聚焦于「制作反映人类双标行为的猫咪表情包梗图」这一特定主题和创意,而sug词条「描改原图」则是一个通用的制作方法,与原始问题的核心创意和主题无关,稀释了原始问题的聚焦度。\n【最终得分 0.15】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.192
+    },
+    "sug_梗图描改鸡蛋_r2_q5_7": {
+      "type": "sug",
+      "query": "[SUG] 梗图描改鸡蛋",
+      "level": 23,
+      "relevance_score": -0.04000000000000002,
+      "evaluationReason": "【评估对象】词条\"梗图描改鸡蛋\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.35】原始问题意图是「制作」反映人类双标行为的猫咪表情包梗图。Sug词条是「梗图描改鸡蛋」,其中「梗图」与原始问题「梗图」有表面关联,但「描改」并非原始问题的「制作」。两者均涉及动作「制作」/「描改」。sug词条中的「描改」是制作的一种方式,但对象「鸡蛋」与原始问题「猫咪表情包」完全不相关。因此,动机相关但偏离。\n【品类维度 -0.50】原始问题核心对象为「猫咪表情包梗图」与「人类双标行为」,sug词条为「梗图」和「鸡蛋」。两者核心对象「猫咪表情包」和「鸡蛋」完全不匹配,差异悬殊。\n【延伸词维度 -0.15】原始问题聚焦于「猫咪表情包梗图」的「制作」和「反映双标行为」这一主题。sug词条「描改鸡蛋」引入了与原始问题主题无关的「鸡蛋」元素,稀释了原始问题的聚焦度,降低了内容的针对性。\n【最终得分 -0.04】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.192
+    },
+    "sug_抽象梗图描改_r2_q5_8": {
+      "type": "sug",
+      "query": "[SUG] 抽象梗图描改",
+      "level": 23,
+      "relevance_score": 0.192,
+      "evaluationReason": "【评估对象】词条\"抽象梗图描改\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.35】原始问题核心动机是制作梗图,sug词条「抽象梗图描改」包含『梗图』,且『描改』是一种制作梗图的动作。描改和制作有相关性,但不是完全一致或强相关,属于弱相关。\n【品类维度 0.08】原始问题需求“猫咪表情包梗图”这一特定对象,且“反映人类双标行为”是其场景限定。sug词条“抽象梗图描改”是抽象泛化的概念,虽有“梗图”,但核心词“描改”与原始问题的“制作”有区别,且未提及“猫咪”和“双标行为”的限定,覆盖度极低。\n【延伸词维度 -0.15】原始问题聚焦于「猫咪表情包梗图」和「人类双标行为」的结合,而sug词条「抽象梗图描改」中的「抽象」和「描改」与原始问题中的核心对象和动机关联度较低,稀释了原始问题的聚焦度。\n【最终得分 0.19】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.192
+    },
+    "sug_梗图描改过程_r2_q5_9": {
+      "type": "sug",
+      "query": "[SUG] 梗图描改过程",
+      "level": 23,
+      "relevance_score": 0.46,
+      "evaluationReason": "【评估对象】词条\"梗图描改过程\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作梗图\n【动机维度 0.55】原始问题是「制作」梗图,sug词条是「梗图描改过程」。「描改过程」是制作梗图的一种具体方法或步骤,属于制作子集,动机相关。\n【品类维度 0.50】原始问题核心对象为「猫咪表情包梗图」,sug词条核心对象为「梗图描改过程」。「梗图」部分匹配,但sug词条未提及「猫咪表情包」及内容限定「人类双标行为」。\n【延伸词维度 -0.15】原始问题聚焦于「制作反映人类双标行为的猫咪表情包梗图」这一特定主题和内容,而sug词条「描改过程」虽然是制作梗图的一种方法,但它稀释了原始问题中「人类双标行为」和「猫咪表情包」这两个核心内容限定,使其偏离了原始问题的核心目的和聚焦度,属于作用域稀释型。\n【最终得分 0.46】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.192
+    },
+    "q_表情包简笔画_r2_6": {
+      "type": "q",
+      "query": "[Q] 表情包简笔画",
+      "level": 22,
+      "relevance_score": 0.18,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": -1,
+      "domain_type": ""
+    },
+    "sug_表情包简笔画可爱_r2_q6_0": {
+      "type": "sug",
+      "query": "[SUG] 表情包简笔画可爱",
+      "level": 23,
+      "relevance_score": -0.19000000000000003,
+      "evaluationReason": "【评估对象】词条\"表情包简笔画可爱\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是“制作”特定主题的表情包梗图。平台sug词条“表情包简笔画可爱”未包含任何动作意图,因此动机匹配度为0。\n【品类维度 -0.20】原始问题核心对象层为「猫咪表情包梗图」,限定词为「人类双标行为」。sug词条「表情包简笔画可爱」包含对象「表情包」,但此表情包带有「简笔画」「可爱」的限定词,与原始问题中的「猫咪」「梗图」「双标行为」完全不匹配,且存在明显的风格偏差,属于品类错位。\n【延伸词维度 -0.15】原始问题聚焦于「人类双标行为」和「猫咪表情包梗图」的制作,强调内容和主题。sug词条「简笔画可爱」引入了绘画风格和情感倾向,与原始问题的内容和主题关联度较低,稀释了原始问题的核心目的,属于作用域稀释型。\n【最终得分 -0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.18
+    },
+    "sug_表情包简笔画抽象_r2_q6_1": {
+      "type": "sug",
+      "query": "[SUG] 表情包简笔画抽象",
+      "level": 23,
+      "relevance_score": -0.19000000000000003,
+      "evaluationReason": "【评估对象】词条\"表情包简笔画抽象\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是「制作」表情包梗图,而sug词条「表情包简笔画抽象」中没有明确的动作意图。sug词条无法识别动作,因此无法评估动作匹配度。\n【品类维度 -0.20】原始问题核心对象为「猫咪表情包梗图」,限定词为「人类双标行为」。Sug词条为「表情包简笔画抽象」,核心对象是「表情包简笔画」,限定词是「抽象」。sug词条未提及猫咪、梗图及双标行为限定,且简笔画与原始需求的「梗图」差异较大,内容主体错位。\n【延伸词维度 -0.15】原始问题聚焦于「猫咪表情包梗图」的制作,强调「人类双标行为」这一主题。sug词条「简笔画」和「抽象」引入了与原始问题制作主题和风格不符的元素,稀释了原始问题的聚焦度,降低了内容的针对性。\n【最终得分 -0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.18
+    },
+    "sug_表情包简笔画大全_r2_q6_2": {
+      "type": "sug",
+      "query": "[SUG] 表情包简笔画大全",
+      "level": 23,
+      "relevance_score": -0.19000000000000003,
+      "evaluationReason": "【评估对象】词条\"表情包简笔画大全\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是「制作」表情包梗图。sug词条「表情包简笔画大全」无明确的动作意图,因此无法评估动作匹配度。\n【品类维度 -0.20】原始问题核心对象为「猫咪表情包梗图」,限定词为「人类双标行为」。Sug词条为「表情包简笔画大全」,虽包含「表情包」,但限定词「简笔画大全」与原始问题完全不符且无「猫咪」主体词,品类有较大偏差。\n【延伸词维度 -0.15】原始问题聚焦于「人类双标行为」和「猫咪表情包梗图」的特定主题制作。sug词条「简笔画大全」引入了与原始问题主题和目的无关的绘画风格和内容,稀释了原始问题的聚焦度,降低了内容针对性。\n【最终得分 -0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.18
+    },
+    "sug_表情包简笔画哭_r2_q6_3": {
+      "type": "sug",
+      "query": "[SUG] 表情包简笔画哭",
+      "level": 23,
+      "relevance_score": -0.3500000000000001,
+      "evaluationReason": "【评估对象】词条\"表情包简笔画哭\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作梗图\n【动机维度 0.00】原始问题的核心动机是「制作」梗图,sug词条「表情包简笔画哭」没有明确的动作意图。\n【品类维度 -0.40】原始问题核心对象为「猫咪表情包梗图」,限定词为「反映人类双标行为」。sug词条核心对象为「表情包」,限定词为「简笔画哭」。sug词条与原始问题仅对象「表情包」有部分重叠,限定词和更具体的对象描述完全不同且品类错位,关联度低。\n【延伸词维度 -0.15】原始问题聚焦于「人类双标行为」和「猫咪表情包梗图」的制作,强调主题和内容。sug词条「简笔画哭」引入了绘画风格和情绪,与原始问题的核心主题和制作目的关联度较低,稀释了原始问题的聚焦度。\n【最终得分 -0.35】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.18
+    },
+    "sug_表情包简笔画图片_r2_q6_4": {
+      "type": "sug",
+      "query": "[SUG] 表情包简笔画图片",
+      "level": 23,
+      "relevance_score": 0.010000000000000009,
+      "evaluationReason": "【评估对象】词条\"表情包简笔画图片\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题意图是「制作」反映人类双标行为的猫咪表情包梗图,sug词条「表情包简笔画图片」没有明确的动作意图。虽然对象都和「表情包」相关,但sug词条中无动机层,无法评估动机维度匹配度。\n【品类维度 0.05】原始问题需求「猫咪表情包梗图」,侧重主题和内容。sug词条「表情包简笔画图片」仅部分覆盖了核心对象词「表情包」,但错失了核心限定词。简笔画与原始需求关联度较低。\n【延伸词维度 -0.15】原始问题聚焦于「人类双标行为」和「猫咪表情包梗图」的特定主题制作。sug词条「简笔画图片」引入了与原始问题主题无关的绘画风格和图片类型,稀释了原始问题的核心内容和目的,属于作用域稀释型延伸词。\n【最终得分 0.01】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.18
+    },
+    "sug_表情包简笔画人物_r2_q6_5": {
+      "type": "sug",
+      "query": "[SUG] 表情包简笔画人物",
+      "level": 23,
+      "relevance_score": -0.19000000000000003,
+      "evaluationReason": "【评估对象】词条\"表情包简笔画人物\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题的核心动机是「制作」表情包梗图,而sug词条「表情包简笔画人物」没有明确的动作意图。\n【品类维度 -0.20】原始问题核心对象为「猫咪表情包梗图」,限定词为「人类双标行为」。sug词核心对象为「简笔画人物」和「表情包」,对象虽有重叠但「人物」和「猫咪」冲突,且缺失所有限定词,语义偏离。\n【延伸词维度 -0.15】原始问题聚焦于「猫咪表情包梗图」和「人类双标行为」的结合,而sug词条「简笔画人物」引入了与原始问题核心对象「猫咪」和主题「双标行为」无关的绘画风格和对象,稀释了原始问题的聚焦度,降低了内容针对性。\n【最终得分 -0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.18
+    },
+    "sug_表情包简笔画教程_r2_q6_6": {
+      "type": "sug",
+      "query": "[SUG] 表情包简笔画教程",
+      "level": 23,
+      "relevance_score": 0.18,
+      "evaluationReason": "【评估对象】词条\"表情包简笔画教程\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.35】原始问题的核心动机是「制作」表情包梗图。sug词条的动机亦为「制作/教程」,两者在制作层面是相关的,但sug词条「简笔画」并未完全涵盖原始问题的表情包梗图,因此是弱相关。\n【品类维度 0.05】原始问题涉及“猫咪表情包梗图”和“人类双标行为”两个主要内容主体;sug词只提及“表情包”,缺失核心对象“猫咪”及所有限定词,且存在对象错位(猫咪vs简笔画)\n【延伸词维度 -0.15】原始问题聚焦于「人类双标行为」和「猫咪表情包梗图」的特定主题创作,而sug词条「简笔画教程」引入了与原始问题主题和目的关联度较低的绘画技巧,稀释了创作主题的聚焦性,属于作用域稀释型。\n【最终得分 0.18】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.18
+    },
+    "sug_表情简笔画_r2_q6_7": {
+      "type": "sug",
+      "query": "[SUG] 表情简笔画",
+      "level": 23,
+      "relevance_score": 0.010000000000000009,
+      "evaluationReason": "【评估对象】词条\"表情简笔画\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是「制作」表情包梗图,而sug词条「表情简笔画」中没有明确的动作意图。sug词条提供了制作表情包的一种「素材」或「风格」,但未展现制作的动作,因此动机维度评分为0。\n【品类维度 0.05】原始问题涉及「猫咪表情包梗图」,而sug词条是「表情简笔画」。两者主题在「表情」上有轻微关联,但猫咪、梗图、双标行为等核心限定都没有体现,匹配度低。\n【延伸词维度 -0.15】原始问题聚焦于「猫咪表情包梗图」的「制作」和「反映双标行为」这一特定主题。sug词条「简笔画」作为一种绘画风格,与原始问题中的「猫咪表情包梗图」制作方法相关,但其过于宽泛,且未提及「猫咪」和「双标行为」的核心要素,稀释了原始问题的核心目的和聚焦度,属于作用域稀释型。\n【最终得分 0.01】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.18
+    },
+    "sug_表情包简笔画加油_r2_q6_8": {
+      "type": "sug",
+      "query": "[SUG] 表情包简笔画加油",
+      "level": 23,
+      "relevance_score": -0.09500000000000001,
+      "evaluationReason": "【评估对象】词条\"表情包简笔画加油\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.00】sug词条「表情包简笔画加油」的动机是“简笔画(表情包)”或“给表情包加油”,与原始问题“制作表情包梗图”的动机“制作”无关,方向不一致。\n【品类维度 -0.20】原始问题涉及「猫咪表情包梗图」这一核心主体。Sug词条仅有「表情包」与核心主体部分匹配,但增加了「简笔画」「加油」等限定词,与原始问题完全不匹配,甚至存在语义错位。\n【延伸词维度 -0.15】原始问题聚焦于「人类双标行为」和「猫咪表情包梗图」的制作,强调主题和内容。sug词条「简笔画」和「加油」与原始问题的核心主题和制作目的无关,稀释了原始问题的聚焦度,降低了内容的针对性。\n【最终得分 -0.10】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.18
+    },
+    "sug_表情包简笔画开心_r2_q6_9": {
+      "type": "sug",
+      "query": "[SUG] 表情包简笔画开心",
+      "level": 23,
+      "relevance_score": -0.09500000000000001,
+      "evaluationReason": "【评估对象】词条\"表情包简笔画开心\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作梗图,表达含义\n【动机维度 0.00】原始问题意图是“制作”具有特定内涵的表情包梗图,sug词条「表情包简笔画开心」侧重“绘画”这一动作,并且没有明确的情绪表达目的,无法匹配,故得分为0。\n【品类维度 -0.20】原始问题核心对象为「人类双标行为的猫咪表情包梗图」。sug词条「表情包简笔画开心」对象为「表情包」,但限定词与原始问题无关且对象带有「简笔画」属性,与要求不符。\n【延伸词维度 -0.15】sug词条「简笔画」和「开心」与原始问题「反映人类双标行为的猫咪表情包梗图」的核心目的和主题不符,稀释了原始问题的聚焦度,降低了内容的针对性。\n【最终得分 -0.10】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.18
+    },
+    "q_双标图片_r2_7": {
+      "type": "q",
+      "query": "[Q] 双标图片",
+      "level": 22,
+      "relevance_score": 0.17,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": -1,
+      "domain_type": ""
+    },
+    "sug_讽刺双标的图片_r2_q7_0": {
+      "type": "sug",
+      "query": "[SUG] 讽刺双标的图片",
+      "level": 23,
+      "relevance_score": 0.49,
+      "evaluationReason": "【评估对象】词条\"讽刺双标的图片\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(猫咪表情包梗图),这个表情包梗图需用于反映/讽刺人类双标行为,因此‘制作’是核心动机,‘反映/讽刺’是目的。\n【动机维度 0.00】原始问题意图是“制作”表情包梗图,而sug词条「讽刺双标的图片」是名词描述,无明确动作意图。sug词条未包含原始问题的核心动机。\n【品类维度 0.65】原始问题核心对象为「猫咪表情包梗图」,限定词为「反映人类双标行为」。sug词核心对象为「图片」,限定词为「讽刺双标」。其中「图片」是「梗图」的泛化,且「讽刺双标」与「反映人类双标行为」高度匹配,但缺失核心对象「猫咪」的限定。覆盖度较高,但有一定泛化和缺失,故给0.65分。\n【延伸词维度 -0.15】原始问题聚焦于「猫咪表情包梗图」这一特定形式,而sug词条「图片」泛化了对象,稀释了原始问题的具体性和趣味性,属于作用域稀释型。\n【最终得分 0.49】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.17
+    },
+    "sug_暗示双标的图片_r2_q7_1": {
+      "type": "sug",
+      "query": "[SUG] 暗示双标的图片",
+      "level": 23,
+      "relevance_score": 0.24999999999999997,
+      "evaluationReason": "【评估对象】词条\"暗示双标的图片\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是「制作」表情包梗图,而sug词条「暗示双标的图片」中不包含明确的动作意图,因此无法评估动作匹配度。\n【品类维度 0.35】sug词条只包含了原始问题中的【双标】和【图】两个核心概念,缺失了特指的【猫咪表情包】。sug词条范围过于宽泛,导致内容主体性匹配度不高。\n【延伸词维度 -0.15】原始问题聚焦于「猫咪表情包梗图」这一特定形式,而sug词条「暗示双标的图片」将范围扩大到所有图片,稀释了原始问题的核心对象「猫咪表情包梗图」,降低了内容的针对性。\n【最终得分 0.25】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.17
+    },
+    "sug_双标搞笑图片_r2_q7_2": {
+      "type": "sug",
+      "query": "[SUG] 双标搞笑图片",
+      "level": 23,
+      "relevance_score": 0.29000000000000004,
+      "evaluationReason": "【评估对象】词条\"双标搞笑图片\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】sug词条《双标搞笑图片》无明确动作意图,只包含对象和场景信息,无法评估其动作匹配度。\n【品类维度 0.40】原始问题对象层为「猫咪表情包梗图」和「人类双标行为」,场景层无。Sug词条包含「双标」和「图片」,与原始问题中的核心要素有部分重合,但缺失「猫咪」和「梗图」的特定性。\n【延伸词维度 -0.15】原始问题聚焦于「猫咪表情包梗图」的制作,强调「人类双标行为」这一主题。sug词条「搞笑图片」虽然与「梗图」有一定关联,但缺少了「猫咪」和「表情包」这两个核心对象,且「制作」的动机也未体现,稀释了原始问题的具体性和目的性。\n【最终得分 0.29】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.17
+    },
+    "sug_讽刺双标的人的文案_r2_q7_3": {
+      "type": "sug",
+      "query": "[SUG] 讽刺双标的人的文案",
+      "level": 23,
+      "relevance_score": -0.14,
+      "evaluationReason": "【评估对象】词条\"讽刺双标的人的文案\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 -0.05】原始问题的核心动机是「制作」表情包梗图。sug词条的动机是「讽刺双标的人的文案」,这是一种「创作」行为或「寻找」行为,与原始问题的「制作」方向有轻微偏离。原始问题侧重行为主体,sug词条侧重内容主题。\n【品类维度 -0.25】原始问题核心对象是「猫咪表情包梗图」,场景限定为「人类双标行为」。sug词条核心对象为「文案」,场景限定为「讽刺双标的人」。对象错位,且场景限定差异较大。\n【延伸词维度 -0.15】原始问题聚焦于「制作猫咪表情包梗图」这一具体行为和对象,延伸词「文案」和「讽刺双标的人」偏离了制作表情包的动作和猫咪这一核心对象,稀释了原始问题的聚焦度。\n【最终得分 -0.14】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.17
+    },
+    "sug_双标梗_r2_q7_4": {
+      "type": "sug",
+      "query": "[SUG] 双标梗",
+      "level": 23,
+      "relevance_score": 0.27999999999999997,
+      "evaluationReason": "【评估对象】词条\"双标梗\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】sug词条「双标梗」是名词短语,无法识别出明确的动作意图,因此无法与原始问题的动作「制作」进行动机匹配,得分为0。\n【品类维度 0.35】原始问题涉及“双标行为”和“梗图”,Sug词条包含“双标梗”,对象层有部分匹配,但缺失了核心的“猫咪表情包”限定,覆盖度较低。\n【延伸词维度 0.00】sug词条未引入延伸词,所有词汇均属于原始问题作用域范围。\n【最终得分 0.28】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.17
+    },
+    "sug_双标是什么意思_r2_q7_5": {
+      "type": "sug",
+      "query": "[SUG] 双标是什么意思",
+      "level": 23,
+      "relevance_score": 0.0050000000000000044,
+      "evaluationReason": "【评估对象】词条\"双标是什么意思\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】「制作」猫咪表情包梗图,用于「反映」人类双标行为。核心动作是「制作」和「反映」\n【动机维度 0.00】原始问题的核心动机是「制作」和「反映」。sug词条「双标是什么意思」的核心动机是「了解/理解」。sug词条仅包含原始问题的主题「双标」,但没有围绕原始问题的核心动作「制作」或「反映」提供任何动机支持,因此动机不匹配。\n【品类维度 0.05】原始问题核心是《猫咪表情包梗图制作》,限定词是《人类双标行为》。sug词条仅包含《双标》,与原始问题的核心对象层《猫咪表情包梗图》几乎无关,覆盖度极低。\n【延伸词维度 -0.15】sug词条「双标是什么意思」中的「是什么意思」是延伸词,它将原始问题从「制作」行为转移到「解释」概念,稀释了原始问题制作梗图的聚焦度,降低了内容针对性。\n【最终得分 0.01】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.17
+    },
+    "sug_形容双标人的文案_r2_q7_6": {
+      "type": "sug",
+      "query": "[SUG] 形容双标人的文案",
+      "level": 23,
+      "relevance_score": -0.09500000000000001,
+      "evaluationReason": "【评估对象】词条\"形容双标人的文案\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作梗图,表达双标行为\n【动机维度 0.00】原始问题的动机是「制作」反映双标行为的表情包梗图,sug词条的动机是「形容」双标人的文案。原始问题与sug词条的动作意图完全不同,动机不匹配。\n【品类维度 -0.20】原始问题核心是《猫咪表情包梗图》,sug词条是《文案》,品类完全不匹配。原始问题包含《双标行为/人》的限定,sug词条包含《双标人》的限定,限定词部分重合,但主体偏差大,且sug词条无法涵盖原始问题核心对象。\n【延伸词维度 -0.15】原始问题聚焦于「制作」猫咪表情包梗图,而sug词条「形容双标人的文案」将主题从制作行为和猫咪表情包转移到文案本身,且与「梗图」的视觉表达形式不符,稀释了原始问题的核心目的和对象。\n【最终得分 -0.10】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.17
+    },
+    "sug_内涵双标的文案_r2_q7_7": {
+      "type": "sug",
+      "query": "[SUG] 内涵双标的文案",
+      "level": 23,
+      "relevance_score": 0.010000000000000009,
+      "evaluationReason": "【评估对象】词条\"内涵双标的文案\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作/创造反映人类双标行为的猫咪表情包梗图\n【动机维度 0.00】原始问题的核心动机是\"制作\"带有特定主题(双标行为)的图片内容,而sug词条是\"内涵\"(理解/包含)某种文案。sug词条无明确动作意图\n【品类维度 0.05】原始问题核心对象是《猫咪表情包梗图》,次要对象是《人类双标行为》,sug词条仅包含《双标》这一核心名词,未包含完整的对象层核心词和所有限定词,且存在语义错位。\n【延伸词维度 -0.15】原始问题聚焦于制作「猫咪表情包梗图」以反映「人类双标行为」,而sug词条「内涵双标的文案」将重点从「猫咪表情包梗图」转移到「文案」,且未提及「猫咪」或「表情包」,稀释了原始问题的核心对象和目的,属于作用域稀释型。\n【最终得分 0.01】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.17
+    },
+    "sug_卡皮巴拉100种可爱图片_r2_q7_8": {
+      "type": "sug",
+      "query": "[SUG] 卡皮巴拉100种可爱图片",
+      "level": 23,
+      "relevance_score": -0.56,
+      "evaluationReason": "【评估对象】词条\"卡皮巴拉100种可爱图片\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是“制作”表情包梗图,而sug词条“卡皮巴拉100种可爱图片”没有任何动作或动机。sug词条不包含动作意图,因此动机匹配度为0。\n【品类维度 -0.55】原始问题对象层为「猫咪表情包梗图」,场景层为「人类双标行为」;sug词条对象层为「卡皮巴拉图片」,场景层为「可爱」。二者核心对象和限定词完全不匹配,品类冲突。\n【延伸词维度 -0.60】原始问题聚焦于「制作反映人类双标行为的猫咪表情包梗图」,核心是「猫咪表情包」和「双标行为」的结合,以及「制作」这一动作。sug词条「卡皮巴拉100种可爱图片」引入了「卡皮巴拉」这一全新的对象,且「可爱图片」与「表情包梗图」及「双标行为」的内涵完全不符。这不仅是无关,更是对原始问题核心内容的严重稀释和偏离,导致用户目的完全无法达成,属于强负向延伸。\n【最终得分 -0.56】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.17
+    },
+    "sug_双标_r2_q7_9": {
+      "type": "sug",
+      "query": "[SUG] 双标",
+      "level": 23,
+      "relevance_score": 0.2,
+      "evaluationReason": "【评估对象】词条\"双标\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.00】sug词条「双标」是原始问题的部分对象,无明确动作意图,无法评估动作匹配度。\n【品类维度 0.25】sug词条《双标》是原始问题中核心限定词之一,体现了部分场景层信息。但缺失原始问题中的核心对象《表情包》《梗图》以及限定词《人类行为》《猫咪》等,覆盖度较低。\n【延伸词维度 0.00】sug词条未引入延伸词,所有词汇均属于原始问题作用域范围。原始问题中的“双标”是核心概念,sug词条“双标”是其同义词,不构成延伸。\n【最终得分 0.20】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.17
+    },
+    "q_表情包图片大全_r2_8": {
+      "type": "q",
+      "query": "[Q] 表情包图片大全",
+      "level": 22,
+      "relevance_score": 0.17,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": -1,
+      "domain_type": ""
+    },
+    "sug_表情包图片大全简笔画_r2_q8_0": {
+      "type": "sug",
+      "query": "[SUG] 表情包图片大全简笔画",
+      "level": 23,
+      "relevance_score": -0.19000000000000003,
+      "evaluationReason": "【评估对象】词条\"表情包图片大全简笔画\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题的核心动机是「制作」表情包梗图,而sug词条并没有包含明确的动作意图。因此,sug词条与原始问题在动机上不匹配。\n【品类维度 -0.20】原始问题核心对象是“猫咪表情包梗图”,限定词是“反映人类双标行为”。Sug词为“表情包图片大全简笔画”,“表情包”部分匹配,但“简笔画”与原始问题的主体“猫咪”和限定词“双标行为梗图”完全不符。\n【延伸词维度 -0.15】原始问题聚焦于「制作」反映「人类双标行为」的「猫咪表情包梗图」,强调内容和创意。sug词条「图片大全」和「简笔画」与原始问题的核心目的「制作」和「内容创意」关联度低,反而引入了无关的素材形式和风格,稀释了原始问题的聚焦度,降低了内容的针对性。\n【最终得分 -0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.17
+    },
+    "sug_表情包图片大全微信_r2_q8_1": {
+      "type": "sug",
+      "query": "[SUG] 表情包图片大全微信",
+      "level": 23,
+      "relevance_score": -0.12000000000000001,
+      "evaluationReason": "【评估对象】词条\"表情包图片大全微信\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 -0.05】原始问题意图是「制作」特定内容的表情包,而Sug词条是「获取/寻找」表情包图片大全。两种意图虽都与表情包相关,但动作方向不同,一个为制作,一个为寻找。\n【品类维度 -0.20】原始问题核心对象为「人类双标行为的猫咪表情包梗图」,场景限定为「反映人类双标行为」。sug词条「表情包图片大全微信」核心对象为「表情包图片」,限定词为「大全」、「微信」。sug词仅泛化匹配到「表情包图片」,但原始问题有更具体的限定,且sug词的限定「微信」与原始问题无关。这种泛化且包含无关限定导致偏离。\n【延伸词维度 -0.15】sug词条「图片大全」和「微信」与原始问题「制作反映人类双标行为的猫咪表情包梗图」的核心目的和作用域完全不符,引入了无关信息,稀释了原始问题的聚焦度,降低了内容针对性。\n【最终得分 -0.12】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.17
+    },
+    "sug_表情包图片大全抽象_r2_q8_2": {
+      "type": "sug",
+      "query": "[SUG] 表情包图片大全抽象",
+      "level": 23,
+      "relevance_score": -0.19000000000000003,
+      "evaluationReason": "【评估对象】词条\"表情包图片大全抽象\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题的核心动机是「制作」表情包,而sug词条「表情包图片大全抽象」没有明确的动机,无法与原始问题进行动机匹配。\n【品类维度 -0.20】原始问题求《猫咪表情包梗图》,含对象「猫咪表情包」和限定「梗图」。sug词条为《表情包图片大全抽象》,对象为「表情包图片」,限定为「大全」和「抽象」。sug词仅泛化匹配对象「表情包」,但限定词与原始问题不相关、甚至有轻微偏差,缺失核心「猫咪」和「梗图」。\n【延伸词维度 -0.15】原始问题聚焦于「制作」特定主题的「猫咪表情包梗图」,而sug词条「图片大全抽象」引入了与制作无关的「大全」和与猫咪表情包主题不符的「抽象」概念,稀释了原始问题的聚焦度。\n【最终得分 -0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.17
+    },
+    "sug_表情包图片大全可爱_r2_q8_3": {
+      "type": "sug",
+      "query": "[SUG] 表情包图片大全可爱",
+      "level": 23,
+      "relevance_score": 0.010000000000000009,
+      "evaluationReason": "【评估对象】词条\"表情包图片大全可爱\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是「制作」表情包梗图。平台sug词条「表情包图片大全可爱」不包含明确的动作意图。因此,动机维度不匹配,得分为0。\n【品类维度 0.05】sug词条仅包含核心对象“表情包”,但缺失了原始问题中的所有限定词,如“人类双标行为”、“猫咪”、“梗图”。对象匹配度低,且缺失所有场景和具体修饰,导致覆盖度低。\n【延伸词维度 -0.15】原始问题聚焦于「制作」特定主题的「猫咪表情包梗图」,强调「人类双标行为」这一核心概念。sug词条「表情包图片大全可爱」中的「图片大全」和「可爱」是延伸词,它们稀释了原始问题中「制作」和「人类双标行为」的特定性与目的性,将需求泛化为普通的表情包浏览,降低了内容的针对性。\n【最终得分 0.01】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.17
+    },
+    "sug_表情包图片大全动态_r2_q8_4": {
+      "type": "sug",
+      "query": "[SUG] 表情包图片大全动态",
+      "level": 23,
+      "relevance_score": -0.19000000000000003,
+      "evaluationReason": "【评估对象】词条\"表情包图片大全动态\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题的核心动机是“制作”,而sug词条无明确动作意图,只提供了“图片大全”的搜索方向。因此,sug词条与原始问题的制作动机不匹配。\n【品类维度 -0.20】原始问题主体为《人类双标行为的猫咪表情包梗图》,sug词条主体为《表情包图片》。sug词条过度泛化,丢失了核心对象《猫咪》和限定词《人类双标行为》、《梗图》,且仅提及“图片”而忽略了“动态”,存在错位。\n【延伸词维度 -0.15】原始问题聚焦于「制作」特定主题的「猫咪表情包梗图」,而sug词条「图片大全动态」引入了「图片大全」和「动态」这两个延伸词。这两个延伸词与原始问题的「制作」和「梗图」目的关联度低,稀释了原始问题的聚焦度,降低了内容针对性。\n【最终得分 -0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.17
+    },
+    "sug_表情包图片大全素材_r2_q8_5": {
+      "type": "sug",
+      "query": "[SUG] 表情包图片大全素材",
+      "level": 23,
+      "relevance_score": 0.19400000000000003,
+      "evaluationReason": "【评估对象】词条\"表情包图片大全素材\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题意图是「制作」表情包,sug词条「表情包图片大全素材」无明确动作意图,仅提供素材,与制作动作不匹配。\n【品类维度 0.28】sug词条「表情包图片大全素材」包含了原始问题中的核心对象词「表情包」和「素材」。但缺失了所有的限定词,包括「人类双标行为」、「猫咪」、「梗图」等」等,覆盖度较低,故中低分。\n【延伸词维度 -0.15】原始问题聚焦于「制作」特定主题的「猫咪表情包梗图」,强调「人类双标行为」这一创意核心。sug词条「表情包图片大全素材」中的「图片大全素材」是延伸词,它将原始问题从「制作」行为和「双标行为」主题,稀释为泛泛的「素材」获取,降低了内容的针对性和创意性,属于作用域稀释型。\n【最终得分 0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.17
+    },
+    "sug_表情包图片大全搞笑_r2_q8_6": {
+      "type": "sug",
+      "query": "[SUG] 表情包图片大全搞笑",
+      "level": 23,
+      "relevance_score": 0.034,
+      "evaluationReason": "【评估对象】词条\"表情包图片大全搞笑\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包\n【动机维度 0.00】原始问题意图是「制作」反映特定主题的表情包。sug词条「图片大全」没有明确的动作,无法评估动作匹配度。\n【品类维度 0.08】原始问题涉及“猫咪表情包梗图”的特定制作目的,sug词条“表情包图片大全搞笑”是通用概念,仅包含“表情包图片”,缺失核心主体“猫咪”和“梗图”等限定,且无明确限定词,覆盖度低。\n【延伸词维度 -0.15】原始问题聚焦于「制作」特定主题的「猫咪表情包梗图」,强调「人类双标行为」这一核心概念。「图片大全搞笑」作为延伸词,与原始问题中的「制作」和「人类双标行为」主题无关,稀释了原始问题的目的性和聚焦度,属于作用域稀释型。\n【最终得分 0.03】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.17
+    },
+    "sug_ch表情包图片大全_r2_q8_7": {
+      "type": "sug",
+      "query": "[SUG] ch表情包图片大全",
+      "level": 23,
+      "relevance_score": -0.039999999999999994,
+      "evaluationReason": "【评估对象】词条\"ch表情包图片大全\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是“制作”表情包梗图,而sug词条「ch表情包图片大全」仅涉及“图片大全”的“获取”或“查看”或“寻找”,无明确制作类动作意图,因此不匹配。\n【品类维度 0.05】原始问题对象层为「猫咪表情包梗图」,场景层为「反映人类双标行为」。Sug词条为「ch表情包图片大全」,对象层为「表情包图片」。sug词条仅在对象层有部分重合,无猫咪、无双标行为等限定,主体过于泛化。\n【延伸词维度 -0.60】原始问题聚焦于「制作反映人类双标行为的猫咪表情包梗图」这一特定主题和创作行为。sug词条「ch表情包图片大全」中的「ch」和「图片大全」均为延伸词。「ch」与原始问题无任何关联,属于无关信息。「图片大全」虽然与「表情包」相关,但原始问题是「制作」,而非「获取大全」,且未限定「ch」类型,因此「图片大全」稀释了原始问题的创作目的,并引入了不相关的限定。\n【最终得分 -0.04】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.17
+    },
+    "sug_开心表情包图片大全_r2_q8_8": {
+      "type": "sug",
+      "query": "[SUG] 开心表情包图片大全",
+      "level": 23,
+      "relevance_score": 0.010000000000000009,
+      "evaluationReason": "【评估对象】词条\"开心表情包图片大全\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.00】原始问题是“制作”表情包梗图,而sug词条是“开心表情包图片大全”,sug词条仅提供了表情包本身作为对象,无任何意图动作,无法匹配原始问题的制作意图。\n【品类维度 0.05】原始问题需求是制作『反映人类双标行为的猫咪表情包梗图』,sug词条仅有『开心表情包图片大全』,只覆盖了对象层中的“表情包”,但丢失了“猫咪”、“梗图”、“双标行为”等所有核心限定词,为过度泛化词,故得分较低分值,得分低。\n【延伸词维度 -0.15】原始问题聚焦于「制作」特定主题的「猫咪表情包梗图」,而sug词条「开心表情包图片大全」引入了「开心」这一情绪限定,且将「制作」目的替换为「图片大全」的浏览需求,稀释了原始问题的核心目的和主题。\n【最终得分 0.01】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.17
+    },
+    "sug_表情包图片大全打印素材_r2_q8_9": {
+      "type": "sug",
+      "query": "[SUG] 表情包图片大全打印素材",
+      "level": 23,
+      "relevance_score": -0.23,
+      "evaluationReason": "【评估对象】词条\"表情包图片大全打印素材\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题核心动机是「制作」反映双标行为的猫咪表情包梗图,sug词条「表情包图片大全打印素材」无明确的动作意图。\n【品类维度 -0.25】原始问题涉及“猫咪表情包梗图”和“双标行为”语义,sug词为“表情包图片大全打印素材”,主体词“表情包”匹配,但限定词“猫咪”、“双标行为”完全不匹配,反而增加了“图片大全”、“打印”、“素材”等不相关限定,造成核心语义偏离。\n【延伸词维度 -0.15】原始问题聚焦于「制作」特定主题的「猫咪表情包梗图」,而sug词条「图片大全」和「打印素材」与原始问题的「制作」和「反映人类双标行为的猫咪表情包梗图」的核心目的关联度低,且「打印素材」引入了与制作无关的新维度,稀释了原始问题的聚焦度。\n【最终得分 -0.23】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.17
+    },
+    "q_梗图meme_r2_9": {
+      "type": "q",
+      "query": "[Q] 梗图meme",
+      "level": 22,
+      "relevance_score": 0.17,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": -1,
+      "domain_type": ""
+    },
+    "sug_梗图meme双人_r2_q9_0": {
+      "type": "sug",
+      "query": "[SUG] 梗图meme双人",
+      "level": 23,
+      "relevance_score": 0.17,
+      "evaluationReason": "【评估对象】词条\"梗图meme双人\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是“制作”梗图,而sug词条「梗图meme双人」是纯名词短语,无法识别出任何明确的动作意图。因此,sug词条在该维度不具有评估价值。\n【品类维度 0.25】原始问题涉及“猫咪表情包梗图”,sug词条仅包含“梗图”,对象层匹配度低,且缺失所有限定词(人类双标行为、猫咪)。\n【延伸词维度 -0.15】延伸词“meme”是“梗图”的同义词,不构成延伸。延伸词“双人”与原始问题中的“猫咪表情包”和“人类双标行为”无关,稀释了原始问题的聚焦度,降低了内容针对性。\n【最终得分 0.17】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.17
+    },
+    "sug_梗图meme模板_r2_q9_1": {
+      "type": "sug",
+      "query": "[SUG] 梗图meme模板",
+      "level": 23,
+      "relevance_score": 0.010000000000000009,
+      "evaluationReason": "【评估对象】词条\"梗图meme模板\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题意图是「制作」反映特定主题的梗图,sug词条「梗图meme模板」只提及了制作的对象,没有包含任何动作意图,动机完全不匹配。\n【品类维度 0.05】原始问题是关于《人类双标行为的猫咪表情包梗图》制作,包含对象层【梗图】和多个限定词。sug词条仅包含对象层【梗图】的泛化词【梗图meme模板】,缺失所有限定词,且匹配度弱。\n【延伸词维度 -0.15】原始问题聚焦于「制作反映人类双标行为的猫咪表情包梗图」这一具体内容和主题,而sug词条「梗图meme模板」则过于宽泛,仅提供了制作梗图的通用工具,稀释了原始问题中关于「猫咪表情包」和「人类双标行为」的核心内容,降低了内容的针对性。\n【最终得分 0.01】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.17
+    },
+    "sug_心理疾病meme梗图_r2_q9_2": {
+      "type": "sug",
+      "query": "[SUG] 心理疾病meme梗图",
+      "level": 23,
+      "relevance_score": 0.010000000000000009,
+      "evaluationReason": "【评估对象】词条\"心理疾病meme梗图\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】原始问题的核心动机是「制作」或「生产」一种特定的表情包/梗图,目的是「反映」人类双标行为。\n【动机维度 0.00】原始问题的动机是「制作」或「反映」,而sug词条「心理疾病meme梗图」没有明确的动作意图,仅是名词短语,因此动机维度得分设为0。\n【品类维度 0.05】原始问题核心对象是“人类双标行为”结合“猫咪表情包梗图”,sug词条核心对象是“心理疾病meme梗图”。两者都是梗图,但核心内容主体风马牛不相及,仅“梗图”是共通对象,无法有效匹配。因此给低分。\n【延伸词维度 -0.15】原始问题聚焦于「猫咪表情包梗图」和「人类双标行为」,而sug词条引入了「心理疾病」这一完全不相关的概念,稀释了原始问题的核心主题,属于作用域稀释型。\n【最终得分 0.01】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.17
+    },
+    "sug_梗图meme素材_r2_q9_3": {
+      "type": "sug",
+      "query": "[SUG] 梗图meme素材",
+      "level": 23,
+      "relevance_score": 0.37,
+      "evaluationReason": "【评估对象】词条\"梗图meme素材\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是「制作」表情包梗图,而sug词条「梗图meme素材」中不包含明确的动作意图,因此无法进行动作匹配度的评估,动机维度得分为0。\n【品类维度 0.50】原始问题对象层为「人类双标行为的猫咪表情包梗图」,sug词条对象层为「梗图meme素材」。sug词仅覆盖「梗图」且泛化为「素材」,缺失全部限定词,但核心对象匹配度匹配。\n【延伸词维度 -0.15】原始问题聚焦于「制作」特定主题的「猫咪表情包梗图」,强调创作过程和内容。sug词条「梗图meme素材」将重点从「制作」转移到「素材」,且「meme」与「梗图」重复,稀释了原始问题中「制作」这一核心动机,并引入了原始问题未明确提及的「素材」这一新维度,降低了内容的针对性。\n【最终得分 0.37】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.17
+    },
+    "sug_梗图meme悲伤自嘲_r2_q9_4": {
+      "type": "sug",
+      "query": "[SUG] 梗图meme悲伤自嘲",
+      "level": 23,
+      "relevance_score": 0.17,
+      "evaluationReason": "【评估对象】词条\"梗图meme悲伤自嘲\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作/反映\n【动机维度 0.00】原始问题意图是「制作」反映人类双标行为的猫咪表情包梗图。sug词条「梗图meme悲伤自嘲」不包含明确的动作意图。\n【品类维度 0.25】原始问题对象层为「表情包梗图」,场景层为「猫咪」和「反映人类双标行为」。sug词条包含「梗图」,但其限定词「悲伤自嘲」与原始问题的限定词相去甚远,且缺失「猫咪」这一核心场景元素。\n【延伸词维度 -0.15】原始问题聚焦于制作「反映人类双标行为的猫咪表情包梗图」,强调了制作方法和具体内容。sug词条「悲伤自嘲」引入了新的情感维度,与原始问题中「双标行为」和「猫咪表情包」的核心内容关联度较低,稀释了原始问题的聚焦度,属于作用域稀释型。\n【最终得分 0.17】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.17
+    },
+    "sug_梗图meme代餐_r2_q9_5": {
+      "type": "sug",
+      "query": "[SUG] 梗图meme代餐",
+      "level": 23,
+      "relevance_score": 0.17,
+      "evaluationReason": "【评估对象】词条\"梗图meme代餐\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题的核心动机是\"制作\"但sug词条\"梗图meme代餐\"中未包含任何动作意图,因此无法进行动作匹配。\n【品类维度 0.25】原始问题对象层为“猫咪表情包梗图”,场景层为“反映人类双标行为”。Sug词条只有“梗图”,对象层匹配度低,限定词完全缺失,过于宽泛泛匹配。\n【延伸词维度 -0.15】sug词条「meme代餐」与原始问题「猫咪表情包梗图」在概念上存在重叠,但「代餐」一词引入了新的、与原始问题制作梗图目的不符的维度,稀释了原始问题的聚焦度,降低了内容的针对性。\n【最终得分 0.17】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.17
+    },
+    "sug_梗图meme学习_r2_q9_6": {
+      "type": "sug",
+      "query": "[SUG] 梗图meme学习",
+      "level": 23,
+      "relevance_score": 0.18,
+      "evaluationReason": "【评估对象】词条\"梗图meme学习\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.35】原始问题的核心动机是学习「制作」梗图,sug词条的动机是「学习」梗图。制作和学习梗图在动机上是弱相关的,学习可以为制作提供理论基础,但并非直接同义或强相关。\n【品类维度 0.05】原始问题核心对象是「反映人类双标行为的猫咪表情包梗图」,sug词条仅包含通用词「梗图」,缺失核心对象「猫咪表情包」及限定词。匹配度很低。\n【延伸词维度 -0.15】原始问题聚焦于「制作」特定主题的「猫咪表情包梗图」,而sug词条的「meme学习」虽然与「梗图」相关,但「学习」这一动机与原始问题的「制作」存在偏差,且「meme」是「梗图」的同义词,未引入新的有益信息,反而稀释了原始问题中「猫咪表情包」和「双标行为」的特定主题,导致聚焦度下降。\n【最终得分 0.18】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.17
+    },
+    "sug_焦虑症meme梗图_r2_q9_7": {
+      "type": "sug",
+      "query": "[SUG] 焦虑症meme梗图",
+      "level": 23,
+      "relevance_score": -0.19000000000000003,
+      "evaluationReason": "【评估对象】词条\"焦虑症meme梗图\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.00】原始问题意图是「制作」反映人类双标行为的猫咪表情包梗图。Sug词条「焦虑症meme梗图」无明确动作意图,因此动机匹配度为0。\n【品类维度 -0.20】原始问题核心对象是《猫咪表情包梗图》和《人类双标行为》,sug词是《焦虑症meme梗图》。sug词的主体《焦虑症》与原始问题主体《猫咪》和《人类双标行为》基本无关联,品类冲突。\n【延伸词维度 -0.15】sug词条「焦虑症」和「meme」与原始问题中的「人类双标行为」和「猫咪表情包梗图」无直接关联,引入了不相关的概念,稀释了原始问题的聚焦度。\n【最终得分 -0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.17
+    },
+    "sug_精神疾病meme梗图_r2_q9_8": {
+      "type": "sug",
+      "query": "[SUG] 精神疾病meme梗图",
+      "level": 23,
+      "relevance_score": -0.19000000000000003,
+      "evaluationReason": "【评估对象】词条\"精神疾病meme梗图\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是【制作】表情包梗图,sug词条无法识别明确的动作意图。虽然都与meme梗图有关,但动机维度上sug词条未覆盖原始问题的动作需求,因此动机维度评分为0。\n【品类维度 -0.20】原始问题核心对象为「猫咪表情包梗图」,限定词为「反映人类双标行为」。sug词条核心对象为「梗图」,限定词为「精神疾病meme」。二者对象虽然都包含「梗图」,但其核心限定词「猫咪表情包」和「精神疾病meme」完全不同,且有误导性。\n【延伸词维度 -0.15】原始问题聚焦于「人类双标行为」和「猫咪表情包梗图」的制作,而sug词条「精神疾病meme梗图」引入了与原始问题主题无关的「精神疾病」概念,稀释了原始问题的聚焦度,降低了内容针对性。\n【最终得分 -0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.17
+    },
+    "sug_梗图meme原创_r2_q9_9": {
+      "type": "sug",
+      "query": "[SUG] 梗图meme原创",
+      "level": 23,
+      "relevance_score": 0.654,
+      "evaluationReason": "【评估对象】词条\"梗图meme原创\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.90】原始问题核心动机是“制作”一份特殊的“表情包/梗图”,sug词条「梗图meme原创」也包含“原创/制作”和“梗图”,因此动机高度匹配。\n【品类维度 0.08】原始问题涉及“猫咪表情包梗图”的制作,主体是“猫咪表情包梗图”及“人类双标行为”这一限定。sug词条“梗图meme原创”仅包含宽泛的主体“梗图”,缺失了“猫咪表情包”及“人类双标行为”等所有限定词,匹配度低。\n【延伸词维度 0.00】sug词条未引入延伸词,所有词汇均属于原始问题作用域范围。原始问题中的“梗图”与sug词条中的“梗图meme”是同义词,不构成延伸。\n【最终得分 0.65】\n【规则说明】情况4:无延伸词,权重调整为 动机70% + 品类30%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.17
+    },
+    "q_梗图分享_r2_10": {
+      "type": "q",
+      "query": "[Q] 梗图分享",
+      "level": 22,
+      "relevance_score": 0.085,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": -1,
+      "domain_type": ""
+    },
+    "sug_恋与深空同人图资源分享_r2_q10_0": {
+      "type": "sug",
+      "query": "[SUG] 恋与深空同人图资源分享",
+      "level": 23,
+      "relevance_score": -0.36000000000000004,
+      "evaluationReason": "【评估对象】词条\"恋与深空同人图资源分享\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 -0.20】原始问题的核心动机是\"制作\"表情包梗图,而sug词条的动机是\"分享\"资源,两者动作方向不同,但都涉及创作或内容流转,因此得分-0.2。\n【品类维度 -0.50】原始问题是关于《猫咪表情包》制作,sug词是《恋与深空同人图》。两者核心对象和限定词完全不匹配,品类完全冲突,主题完全不同,负相关。\n【延伸词维度 -0.60】原始问题是关于「制作反映人类双标行为的猫咪表情包梗图」,核心是「制作」、「猫咪表情包」、「双标行为」。sug词条「恋与深空同人图资源分享」中的「恋与深空」、「同人图」、「资源分享」均与原始问题的作用域完全不符,引入了全新的、不相关的概念,严重稀释了原始问题的聚焦度,且无任何辅助作用,属于强负向延伸。\n【最终得分 -0.36】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.085
+    },
+    "sug_早安分享图片_r2_q10_1": {
+      "type": "sug",
+      "query": "[SUG] 早安分享图片",
+      "level": 23,
+      "relevance_score": -0.21500000000000002,
+      "evaluationReason": "【评估对象】词条\"早安分享图片\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是「制作」,而sug词条「早安分享图片」的核心动机是「分享」。两者动作意图完全不匹配,且sug词条未包含原始问题制作表情包的任何意图。\n【品类维度 -0.50】原始问题主要对象是“猫咪表情包梗图”和“人类双标行为”,场景限定是“制作”。sug词条是“早安分享图片”,两者对象和场景均不匹配,品类显著错位。\n【延伸词维度 -0.15】原始问题聚焦于「制作反映人类双标行为的猫咪表情包梗图」这一特定创意内容,而sug词条「早安分享图片」则是一个宽泛且与原始问题核心内容无关的日常分享行为。该延伸词引入了与原始问题完全不相关的概念,稀释了原始问题的聚焦度,降低了内容针对性,使其偏离了核心目的。\n【最终得分 -0.22】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.085
+    },
+    "sug_定制分享图_r2_q10_2": {
+      "type": "sug",
+      "query": "[SUG] 定制分享图",
+      "level": 23,
+      "relevance_score": 0.07999999999999997,
+      "evaluationReason": "【评估对象】词条\"定制分享图\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.35】原始问题的核心动机是“制作”表情包梗图,而sug词条是“定制”分享图。“制作”与“定制”虽然都含“创作”之意,但“定制”更侧重个性化或特定需求,与“制作”属弱相关,方向上有一定程度的偏差。\n【品类维度 -0.20】原始问题核心对象为「猫咪表情包梗图」,限定词为「人类双标行为」。sug词条「定制分享图」与核心对象及限定词均不匹配,品类错位。\n【延伸词维度 -0.15】原始问题聚焦于「制作」反映人类双标行为的「猫咪表情包梗图」这一具体内容创作过程。「定制分享图」中的「定制」与「制作」有部分重叠,但「分享图」是结果而非制作过程,且未提及「猫咪表情包梗图」这一核心对象。延伸词「分享图」将重点从「制作」转移到「分享」,且未包含原始问题的核心对象,稀释了原始问题的聚焦度。\n【最终得分 0.08】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.085
+    },
+    "sug_图纸分享_r2_q10_3": {
+      "type": "sug",
+      "query": "[SUG] 图纸分享",
+      "level": 23,
+      "relevance_score": -0.35500000000000004,
+      "evaluationReason": "【评估对象】词条\"图纸分享\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题的核心动机是「制作」反映人类双标行为的猫咪表情包梗图。sug词条「图纸分享」的核心动机是「分享」。两个动作不相关。\n【品类维度 -0.85】原始问题对象层为「猫咪表情包梗图」,场景层为「人类双标行为」;Sug词条对象层为「图纸」。两者核心主体完全不匹配,品类冲突严重,方向完全错误。\n【延伸词维度 -0.15】原始问题是关于「制作」表情包梗图,而sug词条「图纸分享」引入了与制作无关的「分享」行为,且「图纸」与「表情包梗图」概念不符,稀释了原始问题的聚焦度。\n【最终得分 -0.36】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.085
+    },
+    "sug_meme梗图分享_r2_q10_4": {
+      "type": "sug",
+      "query": "[SUG] meme梗图分享",
+      "level": 23,
+      "relevance_score": 0.21000000000000002,
+      "evaluationReason": "【评估对象】词条\"meme梗图分享\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.25】原始问题的核心动机是「制作」meme梗图。sug词条「meme梗图分享」的动机是「分享」。分享是制作后的相关步骤,但并非核心动机,属于弱相关。\n【品类维度 0.25】原始问题对象层为“猫咪表情包梗图”,场景层为“反映人类双标行为”;sug词只包含“meme梗图”,属于对象层的泛化,场景层完全缺失。sug词过度泛化。\n【延伸词维度 -0.15】原始问题聚焦于「制作」特定主题的表情包梗图,而sug词条「分享」则引入了新的行为维度,且未提及猫咪和双标行为,稀释了原始问题的核心目的和主题。\n【最终得分 0.21】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.085
+    },
+    "sug_拼豆豆图纸分享_r2_q10_5": {
+      "type": "sug",
+      "query": "[SUG] 拼豆豆图纸分享",
+      "level": 23,
+      "relevance_score": -0.75,
+      "evaluationReason": "【评估对象】词条\"拼豆豆图纸分享\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作/生成\n【动机维度 -0.70】原始问题的核心动机是「制作反映人类双标行为的猫咪表情包梗图」,sug词条的动机是「分享」拼豆豆图纸,两者动作意图完全不相关,且制作与分享方向相反。\n【品类维度 -0.85】原始问题对象层为「猫咪表情包梗图」,场景层为「人类双标行为」。sug词条对象层为「拼豆豆图纸」,两者在对象层完全不匹配,品类冲突严重,无任何关联。\n【延伸词维度 -0.60】原始问题聚焦于「猫咪表情包梗图」的「制作」和「反映人类双标行为」这一主题。sug词条「拼豆豆图纸分享」引入了完全不相关的「拼豆豆」这一制作形式和「图纸分享」这一行为,与原始问题的核心对象、动机和主题均不符,属于典型的作用域稀释型延伸词,且稀释程度高。\n【最终得分 -0.75】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.085
+    },
+    "sug_买断图分享_r2_q10_6": {
+      "type": "sug",
+      "query": "[SUG] 买断图分享",
+      "level": 23,
+      "relevance_score": -0.5650000000000001,
+      "evaluationReason": "【评估对象】词条\"买断图分享\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作/形成\n【动机维度 -0.70】原始问题核心动机是「制作」或「形成」,而sug词条「买断图分享」的核心动机是「买断」(购买)与「分享」。两者动作意图完全相反。\n【品类维度 -0.50】原始问题是关于“猫咪表情包梗图”的制作,以及其主题“人类双标行为”,对象层为“猫咪表情包梗图”。Sug词为“买断图分享”,仅提及“图”且无任何限定词,与原始问题的核心对象和特定主题均不匹配,存在品类错位。\n【延伸词维度 -0.15】原始问题聚焦于「制作」猫咪表情包梗图,而sug词条「买断图分享」引入了与制作无关的「买断」和「分享」概念,稀释了原始问题的核心目的,属于作用域稀释型。\n【最终得分 -0.57】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.085
+    },
+    "sug_拼豆图纸分享_r2_q10_7": {
+      "type": "sug",
+      "query": "[SUG] 拼豆图纸分享",
+      "level": 23,
+      "relevance_score": -0.38000000000000006,
+      "evaluationReason": "【评估对象】词条\"拼豆图纸分享\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是「制作」表情包梗图。sug词条「拼豆图纸分享」的核心动机是「分享」图纸,两者动作意图完全不匹配。\n【品类维度 -0.80】原始问题是关于《猫咪表情包梗图》制作,涉及对象层《梗图》和场景层《猫咪》、《双标行为》。Sug词是《拼豆图纸分享》,对象层为《图纸》,与原始问题对象《梗图》品类完全不符,无任何关联性。\n【延伸词维度 -0.60】sug词条「拼豆图纸分享」与原始问题「制作反映人类双标行为的猫咪表情包梗图」在主题、内容和目的上完全不相关,引入了与原始问题无关的全新概念,严重稀释了原始问题的聚焦度,导致内容完全偏离,属于作用域稀释型。\n【最终得分 -0.38】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.085
+    },
+    "sug_日常分享图片_r2_q10_8": {
+      "type": "sug",
+      "query": "[SUG] 日常分享图片",
+      "level": 23,
+      "relevance_score": -0.12000000000000001,
+      "evaluationReason": "【评估对象】词条\"日常分享图片\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 -0.05】原始问题意图是「制作」特定内容的表情包梗图。sug词条「日常分享图片」的意图是「分享」图片,虽然都涉及图片,但原始问题是重在创作和产出,sug词条重在传播,两者方向存在偏移。因此,评分为-0.05。\n【品类维度 -0.20】原始问题需「猫咪表情包梗图」,涉及特定内容、主题和形式。sug词「日常分享图片」泛化度过高,无法体现特定创作对象或主题,存在明显错位,故得分较低。\n【延伸词维度 -0.15】sug词条「日常分享图片」中的「日常分享」和「图片」均与原始问题「制作反映人类双标行为的猫咪表情包梗图」的核心目的和具体对象不符,引入了无关信息,稀释了原始问题的聚焦度,属于作用域稀释型延伸词。\n【最终得分 -0.12】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.085
+    },
+    "sug_自截分享图_r2_q10_9": {
+      "type": "sug",
+      "query": "[SUG] 自截分享图",
+      "level": 23,
+      "relevance_score": -0.015,
+      "evaluationReason": "【评估对象】词条\"自截分享图\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.00】原始问题主要动机是《制作》梗图,而sug词条《自截分享图》中《自截》与《制作》有部分关联,但《分享》部分和原始问题不匹配,且无法完全替代原始问题的动机。\n【品类维度 0.00】原始问题主要对象是“猫咪表情包梗图”,限定词是“人类双标行为”;sug词是“自截分享图”。二者内容主体差异极大,无关联性。\n【延伸词维度 -0.15】原始问题是关于「制作」特定主题的表情包梗图,而sug词条「自截分享图」与制作行为无关,且未提及猫咪或双标行为,稀释了原始问题的核心目的和主题。\n【最终得分 -0.01】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.085
+    },
+    "q_猫咪表情包梗图_r2_11": {
+      "type": "q",
+      "query": "[Q] 猫咪表情包梗图",
+      "level": 22,
+      "relevance_score": 0.23399999999999999,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": -1,
+      "domain_type": "D3"
+    },
+    "sug_猫咪表情包_r2_q11_0": {
+      "type": "sug",
+      "query": "[SUG] 猫咪表情包",
+      "level": 23,
+      "relevance_score": 0.48,
+      "evaluationReason": "【评估对象】词条\"猫咪表情包\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包、梗图)\n【动机维度 0.00】原始问题的核心动机是「制作」表情包和梗图,而sug词条「猫咪表情包」是一个名词,没有明确的动作意图,因此无法评估动作匹配度。\n【品类维度 0.60】sug词条「猫咪表情包」与原始问题在对象层「猫咪表情包」完全匹配。虽然sug词条未涵盖原始问题中所有限定词(如「人类双标行为」、「梗图」),导致覆盖度非100%,但核心对象一致。\n【延伸词维度 0.00】sug词条未引入延伸词,所有词汇均属于原始问题作用域范围。\n【最终得分 0.48】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.23399999999999999
+    },
+    "sug_猫咪表情包图片_r2_q11_1": {
+      "type": "sug",
+      "query": "[SUG] 猫咪表情包图片",
+      "level": 23,
+      "relevance_score": 0.52,
+      "evaluationReason": "【评估对象】词条\"猫咪表情包图片\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是“制作”表情包梗图,而sug词条「猫咪表情包图片」仅为一个对象描述,没有体现任何动作意图。sug词条缺失动机层,故动机维度匹配度为0。\n【品类维度 0.65】原始问题对象层为“猫咪表情包梗图”,限定词有“人类双标行为”;Sug词仅包含“猫咪表情包图片”,对象匹配但在场景限定上缺失“人类双标行为”和“梗图”更具体的限定,覆盖度适中。\n【延伸词维度 0.00】sug词条未引入延伸词,所有词汇均属于原始问题作用域范围。原始问题核心是“猫咪表情包”,sug词条中的“猫咪表情包”是核心对象,而“图片”是其表现形式,属于细化,不构成延伸。\n【最终得分 0.52】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.23399999999999999
+    },
+    "sug_猫咪咪表情包图片_r2_q11_2": {
+      "type": "sug",
+      "query": "[SUG] 猫咪咪表情包图片",
+      "level": 23,
+      "relevance_score": 0.41000000000000003,
+      "evaluationReason": "【评估对象】词条\"猫咪咪表情包图片\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】「制作」猫咪表情包梗图,反映人类双标行为\n【动机维度 0.00】原始问题的核心动机是「制作」反映人类双标行为的猫咪表情包梗图。sug词条「猫咪咪表情包图片」没有明确的动作意图。\n【品类维度 0.55】原始问题对象层为“猫咪表情包梗图”,限定词为“反映人类双标行为”;sug词对象层为“猫咪咪表情包图片”,未包含任何限定词。对象层匹配,但sug词未包含限定词,覆盖度低。\n【延伸词维度 -0.15】原始问题聚焦于「制作」反映「人类双标行为」的「猫咪表情包梗图」,强调制作方法和特定主题。sug词条「猫咪咪表情包图片」将「制作」和「人类双标行为」这两个核心动机和限定条件稀释,仅保留了「猫咪表情包」这一对象,且将「梗图」替换为更宽泛的「图片」,降低了内容的针对性和深度,属于作用域稀释型。\n【最终得分 0.41】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.23399999999999999
+    },
+    "sug_猫咪搞笑表情包_r2_q11_3": {
+      "type": "sug",
+      "query": "[SUG] 猫咪搞笑表情包",
+      "level": 23,
+      "relevance_score": 0.44999999999999996,
+      "evaluationReason": "【评估对象】词条\"猫咪搞笑表情包\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是「制作」表情包梗图,而sug词条「猫咪搞笑表情包」并没有体现任何动作意图,因此动机匹配度为0。\n【品类维度 0.60】原始问题对象层为「人类双标行为的猫咪表情包梗图」,场景层无。sug词条对象层为「猫咪搞笑表情包」,场景层无。sug词条的主体词「猫咪表情包」与原始问题主体词「猫咪表情包」匹配,但sug词「搞笑」无法涵盖原始问题「人类双标行为」这一限定。覆盖度计算:1/2=50%。\n【延伸词维度 -0.15】原始问题聚焦于“反映人类双标行为”这一特定主题的猫咪表情包梗图制作。sug词条“搞笑”虽然是表情包的常见属性,但它稀释了原始问题中“双标行为”这一核心概念的聚焦度,使其偏离了原始问题的特定目的,属于作用域稀释型。\n【最终得分 0.45】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.23399999999999999
+    },
+    "sug_猫咪表情包梗图搞笑_r2_q11_4": {
+      "type": "sug",
+      "query": "[SUG] 猫咪表情包梗图搞笑",
+      "level": 23,
+      "relevance_score": 0.6000000000000001,
+      "evaluationReason": "【评估对象】词条\"猫咪表情包梗图搞笑\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.00】原始问题的核心动机是“制作”表情包梗图,而sug词条「猫咪表情包梗图搞笑」并未包含明确的动作意图,仅是对内容或主题的描述,因此无法评估动作匹配度。\n【品类维度 0.75】原始问题对象层为「猫咪表情包梗图」,限定词为「反映人类双标行为」。sug词条「猫咪表情包梗图搞笑」包含了原始问题的完整对象层「猫咪表情包梗图」,但在限定词上,sug词条的「搞笑」是新的限定词,原始问题则无此限定,覆盖度适中故给0.75分。\n【延伸词维度 0.00】sug词条未引入延伸词,所有词汇均属于原始问题作用域范围。\n【最终得分 0.60】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.23399999999999999
+    },
+    "q_猫咪表情包_r2_12": {
+      "type": "q",
+      "query": "[Q] 猫咪表情包",
+      "level": 22,
+      "relevance_score": 0.21059999999999998,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": -1,
+      "domain_type": "D3"
+    },
+    "sug_猫咪表情包抽象_r2_q12_0": {
+      "type": "sug",
+      "query": "[SUG] 猫咪表情包抽象",
+      "level": 23,
+      "relevance_score": 0.034,
+      "evaluationReason": "【评估对象】词条\"猫咪表情包抽象\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.00】sug词条「猫咪表情包抽象」未体现出明确的动作意图和目的,主要描述的是内容主题,因此在动机维度上无法匹配原始问题的「制作」意图。\n【品类维度 0.08】原始问题对象层为「猫咪表情包梗图」,场景层为「反映人类双标行为」。Sug词条包含「猫咪表情包」,但缺失「梗图」和所有场景层限定,覆盖度低且过度泛化。\n【延伸词维度 -0.15】原始问题聚焦于「双标行为」这一特定主题的表情包制作,而sug词条「抽象」引入了新的、更宽泛的风格维度,稀释了原始问题的核心主题,降低了内容的针对性。\n【最终得分 0.03】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.21059999999999998
+    },
+    "sug_猫meme表情包动图_r2_q12_1": {
+      "type": "sug",
+      "query": "[SUG] 猫meme表情包动图",
+      "level": 23,
+      "relevance_score": 0.36,
+      "evaluationReason": "【评估对象】词条\"猫meme表情包动图\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.35】原始问题意图是「制作」反映人类双标行为的猫咪表情包梗图。sug词条「猫meme表情包动图」核心动机是「查找」或「浏览」猫咪相关的表情包动图,与「制作」存在一定程度的间接关联。\n【品类维度 0.50】原始问题核心对象层为「猫咪表情包梗图」,限定词为「反映人类双标行为」。sug词条「猫meme表情包动图」涵盖了核心对象「猫咪表情包」及部分限定词(meme≈梗图),但缺失「双标行为」这一关键限定,且新增「动图」限定。整体匹配度中等。\n【延伸词维度 -0.15】原始问题聚焦于“人类双标行为”这一特定主题的猫咪表情包梗图制作,强调内容创意。sug词条“猫meme表情包动图”将“梗图”具体化为“动图”,并引入了“meme”这一更宽泛的概念,稀释了原始问题中“人类双标行为”这一核心主题的聚焦度,降低了内容的针对性。\n【最终得分 0.36】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.21059999999999998
+    },
+    "sug_猫咪表情包配文_r2_q12_2": {
+      "type": "sug",
+      "query": "[SUG] 猫咪表情包配文",
+      "level": 23,
+      "relevance_score": 0.38,
+      "evaluationReason": "【评估对象】词条\"猫咪表情包配文\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.35】原始问题的核心动作为“制作”,sug词条“配文”是制作表情包过程中所需要的其中一个步骤,属于弱相关\n【品类维度 0.55】原始问题对象层为「猫咪表情包梗图」,限定词为「反映人类双标行为」。Sug词条对象层为「猫咪表情包配文」。对象主体「猫咪表情包」匹配,限定词缺失,但「配文」是「梗图」的重要组成部分。\n【延伸词维度 -0.15】原始问题聚焦于「人类双标行为」和「梗图」的制作,而sug词条「配文」虽然是表情包的一部分,但它将原始问题中「双标行为」和「梗图」的核心概念稀释,使其偏离了原始问题的核心目的,降低了内容的针对性。\n【最终得分 0.38】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.21059999999999998
+    },
+    "sug_猫咪表情包动图_r2_q12_3": {
+      "type": "sug",
+      "query": "[SUG] 猫咪表情包动图",
+      "level": 23,
+      "relevance_score": 0.41000000000000003,
+      "evaluationReason": "【评估对象】词条\"猫咪表情包动图\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题核心动作用于《制作》表情包梗图,sug词条仅提及《表情包》这一对象而无任何动作意图,因此动机匹配度为0。\n【品类维度 0.55】原始问题对象层为“猫咪表情包梗图”,限定词为“反映人类双标行为”;sug词对象层为“猫咪表情包动图”。对象层“猫咪表情包”核心主体匹配,但限定词“梗图”与“动图”不完全一致,且缺失原始问题所有限定词,故中等偏上评分。\n【延伸词维度 -0.15】原始问题聚焦于「反映人类双标行为」的特定主题,而sug词条「动图」引入了无关的格式限定,稀释了主题的聚焦度,降低了内容针对性。\n【最终得分 0.41】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.21059999999999998
+    },
+    "sug_猫咪表情包制作_r2_q12_4": {
+      "type": "sug",
+      "query": "[SUG] 猫咪表情包制作",
+      "level": 23,
+      "relevance_score": 0.825,
+      "evaluationReason": "【评估对象】词条\"猫咪表情包制作\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.90】原始问题的核心动机是「制作」表情包梗图,sug词条「猫咪表情包制作」也包含了「制作」的动作,且对象是「表情包」,动作完全匹配。\n【品类维度 0.65】原始问题对象层为“猫咪表情包梗图”,场景层为“反映人类双标行为”;Sug词条对象层为“猫咪表情包”。Sug词条包含了核心对象“猫咪表情包”,但缺失了重要的场景限定“反映人类双标行为”,覆盖度约为50%,属于部分匹配。\n【延伸词维度 0.00】sug词条未引入延伸词,所有词汇均属于原始问题作用域范围。sug词条是原始问题的简化,未增加新的信息维度。\n【最终得分 0.82】\n【规则说明】情况4:无延伸词,权重调整为 动机70% + 品类30%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.21059999999999998
+    },
+    "sug_猫咪表情包可爱_r2_q12_5": {
+      "type": "sug",
+      "query": "[SUG] 猫咪表情包可爱",
+      "level": 23,
+      "relevance_score": 0.37,
+      "evaluationReason": "【评估对象】词条\"猫咪表情包可爱\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是「制作」表情包梗图,而sug词条「猫咪表情包可爱」没有明确的动作意图,因此无法匹配,得分为0。\n【品类维度 0.50】核心对象层为“猫咪表情包”,sug词条完全命中。原始问题的特有对象层修饰词“反映人类双标行为的”以及场景层“梗图”未命中,覆盖度50%。\n【延伸词维度 -0.15】原始问题聚焦于「反映人类双标行为」的「梗图」制作,强调主题性和创作性。「可爱」作为延伸词,虽然是猫咪表情包的常见属性,但与原始问题中「双标行为」和「梗图」的核心目的关联度低,且可能稀释了原始问题对特定主题和创作手法的聚焦,属于作用域稀释型。\n【最终得分 0.37】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.21059999999999998
+    },
+    "sug_猫咪表情符号_r2_q12_6": {
+      "type": "sug",
+      "query": "[SUG] 猫咪表情符号",
+      "level": 23,
+      "relevance_score": 0.17,
+      "evaluationReason": "【评估对象】词条\"猫咪表情符号\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题意图是“制作”表情包梗图,而sug词条「猫咪表情符号」只提及了一个对象和主题,没有明确的动作意图,无法匹配原始问题的制作动机。\n【品类维度 0.25】原始问题涉及“猫咪+表情包+梗图”以及“人类双标行为”的特定场景,sug词条仅包含“猫咪+表情符号”,对象层部分匹配。场景和具体概念严重缺失,覆盖度很低。虽有“猫咪”和“表情”关联,但深度和广度不足。\n【延伸词维度 -0.15】原始问题聚焦于「制作反映人类双标行为的猫咪表情包梗图」,强调了制作行为、双标主题和梗图形式。「表情符号」作为延伸词,与「表情包梗图」存在差异,稀释了原始问题对「梗图」和「双标行为」的特定需求,降低了内容的针对性。\n【最终得分 0.17】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.21059999999999998
+    },
+    "sug_猫咪表情包叫什么_r2_q12_7": {
+      "type": "sug",
+      "query": "[SUG] 猫咪表情包叫什么",
+      "level": 23,
+      "relevance_score": 0.185,
+      "evaluationReason": "【评估对象】词条\"猫咪表情包叫什么\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动是「制作」表情包,而sug词条「猫咪表情包叫什么」是一个提问,意图是「获取名称信息」。sug词条无明确动机词,与原始问题的制作意图无关。\n【品类维度 0.50】原始问题对象层为「猫咪表情包梗图」,场景层为「反映人类双标行为」。sug词条对象层为「猫咪表情包」,与原始问题核心主体匹配,但缺失关键场景限定词及「梗图」限定。\n【延伸词维度 -0.15】sug词条「叫什么」引入了对猫咪表情包名称的询问,这与原始问题中「制作」表情包的动机和「反映人类双标行为」的对象完全不符,稀释了原始问题的核心目的和聚焦度。\n【最终得分 0.18】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.21059999999999998
+    },
+    "sug_猫咪表情包图片_r2_q12_8": {
+      "type": "sug",
+      "query": "[SUG] 猫咪表情包图片",
+      "level": 23,
+      "relevance_score": 0.52,
+      "evaluationReason": "【评估对象】词条\"猫咪表情包图片\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是“制作”表情包梗图,而sug词条「猫咪表情包图片」仅为一个对象描述,没有体现任何动作意图。sug词条缺失动机层,故动机维度匹配度为0。\n【品类维度 0.65】原始问题对象层为“猫咪表情包梗图”,限定词有“人类双标行为”;Sug词仅包含“猫咪表情包图片”,对象匹配但在场景限定上缺失“人类双标行为”和“梗图”更具体的限定,覆盖度适中。\n【延伸词维度 0.00】sug词条未引入延伸词,所有词汇均属于原始问题作用域范围。原始问题核心是“猫咪表情包”,sug词条中的“猫咪表情包”是核心对象,而“图片”是其表现形式,属于细化,不构成延伸。\n【最终得分 0.52】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.21059999999999998
+    },
+    "sug_猫咪表情包视频_r2_q12_9": {
+      "type": "sug",
+      "query": "[SUG] 猫咪表情包视频",
+      "level": 23,
+      "relevance_score": 0.455,
+      "evaluationReason": "【评估对象】词条\"猫咪表情包视频\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.50】原始问题的核心动机是\"制作\"表情包梗图,而sug词条「猫咪表情包视频」的动作是制作「视频」。虽然都是「制作」大类,但对象由静态的「表情包梗图」变成了动态的「视频」,属于相关动作,但不完全匹配。\n【品类维度 0.55】原始问题对象层为「猫咪表情包梗图」,场景层为「双标行为」。sug词条对象层为「猫咪表情包视频」。对象层核心词“猫咪表情包”匹配,但限定词“梗图”与“视频”不完全匹配,且缺失「双标行为」限定。\n【延伸词维度 -0.15】原始问题是制作“表情包梗图”,强调静态图片和梗的创作。sug词条引入“视频”这一延伸词,改变了原始问题的媒介形式,稀释了对“梗图”制作的聚焦,属于作用域稀释型。\n【最终得分 0.46】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.21059999999999998
+    },
+    "q_猫咪梗图_r2_13": {
+      "type": "q",
+      "query": "[Q] 猫咪梗图",
+      "level": 22,
+      "relevance_score": 0.21059999999999998,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": -1,
+      "domain_type": "D3"
+    },
+    "sug_猫咪梗图素材_r2_q13_0": {
+      "type": "sug",
+      "query": "[SUG] 猫咪梗图素材",
+      "level": 23,
+      "relevance_score": 0.4,
+      "evaluationReason": "【评估对象】词条\"猫咪梗图素材\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题意图是「制作」反映人类双标行为的猫咪梗图,sug词条是「猫咪梗图素材」,sug词条没有明确的动机意图,无法评估动作匹配度。\n【品类维度 0.50】原始问题对象层为“猫咪表情包梗图”,限定词为“人类双标行为”,sug词条仅包含核心对象“猫咪梗图(素材)”,限定词缺失。\n【延伸词维度 0.00】sug词条未引入延伸词,所有词汇均属于原始问题作用域范围。原始问题中的“表情包梗图”与sug词条中的“梗图”是同义词,而“素材”是制作梗图的必要组成部分,属于原始问题作用域内的词汇。\n【最终得分 0.40】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.21059999999999998
+    },
+    "sug_猫咪梗图模板_r2_q13_1": {
+      "type": "sug",
+      "query": "[SUG] 猫咪梗图模板",
+      "level": 23,
+      "relevance_score": 0.4,
+      "evaluationReason": "【评估对象】词条\"猫咪梗图模板\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(动)+ 表情包梗图(对象)+ 反映人类双标行为的猫咪(场景/目的)\n【动机维度 0.00】原始问题的核心动机是“制作”,而sug词条“猫咪梗图模板”中缺失明确的动作意图。\n【品类维度 0.50】原始问题对象层为「人类双标行为的猫咪表情包梗图」,sug词条对象层为「猫咪梗图模板」,仅包含核心对象「猫咪梗图」,但场景层和具体限定词缺失。\n【延伸词维度 0.00】sug词条未引入延伸词,所有词汇均属于原始问题作用域范围。原始问题是制作“猫咪表情包梗图”,sug词条是“猫咪梗图模板”,模板是制作梗图的工具,属于原始问题对象层,因此不构成延伸词。\n【最终得分 0.40】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.21059999999999998
+    },
+    "sug_猫咪梗图骂人_r2_q13_2": {
+      "type": "sug",
+      "query": "[SUG] 猫咪梗图骂人",
+      "level": 23,
+      "relevance_score": 0.34500000000000003,
+      "evaluationReason": "【评估对象】词条\"猫咪梗图骂人\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.40】原始问题的核心动机是「制作」反映特定主题的表情包梗图。sug词条「猫咪梗图骂人」的动机是「制作」或「寻找」猫咪梗图,方向上与原始问题中的「制作」表情包梗图是弱相关的实现路径。\n【品类维度 0.40】sug词条与原始问题都包含核心对象“猫咪梗图”,但sug词条的限定词“骂人”与原始问题的“人类双标行为”语义不匹配,且缺失『表情包』限定,覆盖度较低。\n【延伸词维度 -0.15】原始问题聚焦于制作反映人类双标行为的猫咪表情包梗图,强调「双标行为」这一特定主题。sug词条「骂人」引入了新的、更宽泛且可能负面的情绪表达,稀释了原始问题中「双标行为」这一核心概念的聚焦度,降低了内容的针对性。\n【最终得分 0.35】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.21059999999999998
+    },
+    "sug_猫咪梗图恋爱_r2_q13_3": {
+      "type": "sug",
+      "query": "[SUG] 猫咪梗图恋爱",
+      "level": 23,
+      "relevance_score": -0.19000000000000003,
+      "evaluationReason": "【评估对象】词条\"猫咪梗图恋爱\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题意图是「制作」反映特定主题的猫咪梗图。sug词条「猫咪梗图恋爱」缺失了明确的动机词,无法与「制作」动作匹配,因此动机维度评分为0。\n【品类维度 -0.20】原始问题是关于“人类双标行为”的“猫咪表情包梗图”。Sug词是“猫咪梗图恋爱”。两者核心主体都是“猫咪梗图”,匹配。但原始问题限定词是“人类双标行为”,sug词的限定词是“恋爱”,两者完全不相关且语义偏离,导向错误。\n【延伸词维度 -0.15】原始问题聚焦于「人类双标行为」这一主题的猫咪表情包梗图制作,而sug词条引入了「恋爱」这一新的主题,稀释了原始问题的核心聚焦,使其偏离了「双标行为」的特定内容,降低了内容的针对性。\n【最终得分 -0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.21059999999999998
+    },
+    "sug_猫咪梗图伤感_r2_q13_4": {
+      "type": "sug",
+      "query": "[SUG] 猫咪梗图伤感",
+      "level": 23,
+      "relevance_score": 0.33000000000000007,
+      "evaluationReason": "【评估对象】词条\"猫咪梗图伤感\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】sug词条「猫咪梗图伤感」只有对象(猫咪梗图)和情绪(伤感),没有明确的动作意图,无法与原始问题中的“制作”动作进行匹配。\n【品类维度 0.45】原始问题对象层为「猫咪表情包梗图」,场景层为「反映人类双标行为」。Sug词条对象层为「猫咪梗图」,场景层为「伤感」。核心对象「猫咪梗图」匹配,但场景层差异较大,原始问题限定词「反映人类双标」未覆盖,Sug词引入新限定词「伤感」。\n【延伸词维度 -0.15】原始问题聚焦于「人类双标行为」这一特定主题的猫咪梗图制作,而sug词条「伤感」引入了新的情感维度,与原始问题的核心主题不符,稀释了原始问题的聚焦度。\n【最终得分 0.33】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.21059999999999998
+    },
+    "sug_猫咪梗图睡觉_r2_q13_5": {
+      "type": "sug",
+      "query": "[SUG] 猫咪梗图睡觉",
+      "level": 23,
+      "relevance_score": 0.17,
+      "evaluationReason": "【评估对象】词条\"猫咪梗图睡觉\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题是「制作」反映双标行为的猫咪表情包梗图,核心动作为制作。Sug词条「猫咪梗图睡觉」无明确动作意图,动机维度不匹配。\n【品类维度 0.25】原始问题对象层为“猫咪表情包梗图”,限定词是“反映人类双标行为”;sug词条对象层为“猫咪梗图”,限定词是“睡觉”。二者对象层部分匹配(猫咪梗图),但限定词完全不匹配,语义错位。\n【延伸词维度 -0.15】原始问题聚焦于「制作反映人类双标行为的猫咪表情包梗图」,强调「双标行为」和「表情包」的创作。sug词条「睡觉」引入了与原始问题核心目的无关的新主题,稀释了对「双标行为」和「表情包制作」的聚焦,属于作用域稀释型。\n【最终得分 0.17】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.21059999999999998
+    },
+    "sug_猫咪梗图英文_r2_q13_6": {
+      "type": "sug",
+      "query": "[SUG] 猫咪梗图英文",
+      "level": 23,
+      "relevance_score": 0.29000000000000004,
+      "evaluationReason": "【评估对象】词条\"猫咪梗图英文\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是「制作」表情包梗图。sug词条「猫咪梗图英文」只提到了主题和格式(英文),没有体现任何动作意图,因此动机匹配度为零。\n【品类维度 0.40】原始问题对象层为「猫咪表情包梗图」,场景层为「人类双标行为」。sug词条对象层为「猫咪梗图」,与原始问题核心主体匹配。但sug词条的限定词「英文」与原始问题限定词「人类双标行为」完全不匹配,且「表情包」这一重要限定词也缺失。\n【延伸词维度 -0.15】延伸词“英文”与原始问题“制作反映人类双标行为的猫咪表情包梗图”的核心目的和作用域无关,稀释了用户对制作方法和内容的关注度,属于作用域稀释型。\n【最终得分 0.29】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.21059999999999998
+    },
+    "sug_猫咪梗图塔牌_r2_q13_7": {
+      "type": "sug",
+      "query": "[SUG] 猫咪梗图塔牌",
+      "level": 23,
+      "relevance_score": 0.010000000000000009,
+      "evaluationReason": "【评估对象】词条\"猫咪梗图塔牌\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是「制作」反映人类双标行为的猫咪表情包梗图。sug词条「猫咪梗图塔牌」无法识别明确的动作意图。因此动机维度得分设为0。\n【品类维度 0.05】原始问题对象层为「猫咪表情包梗图」,场景层为「反映人类双标行为」。Sug词条包含「猫咪梗图」,匹配部分对象层,但缺失所有限定词。\n【延伸词维度 -0.15】sug词条中的“塔牌”与原始问题“如何制作反映人类双标行为的猫咪表情包梗图”的核心目的和作用域无关,引入了无关信息,稀释了原始问题的聚焦度,降低了内容的针对性。\n【最终得分 0.01】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.21059999999999998
+    },
+    "sug_猫咪梗图拍照_r2_q13_8": {
+      "type": "sug",
+      "query": "[SUG] 猫咪梗图拍照",
+      "level": 23,
+      "relevance_score": 0.29999999999999993,
+      "evaluationReason": "【评估对象】词条\"猫咪梗图拍照\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.35】原始问题意图是「制作」反映特定行为的「猫咪表情包梗图」。sug词条「猫咪梗图拍照」的动作是「拍照」,这是制作梗图的一个前置或相关动作,但并非核心「制作」意图,属于弱相关。\n【品类维度 0.35】原始问题对象层为《人类双标行为的猫咪表情包梗图》,作用域限定词为《人类双标行为》。sug词条对象层为《猫咪梗图》,限定词缺失且未提及《表情包》和《人类双标行为》。\n【延伸词维度 -0.15】原始问题聚焦于「制作」反映双标行为的「猫咪表情包梗图」,强调内容创作和主题。sug词条「拍照」引入了新的动作,且「拍照」与「制作」表情包梗图并非完全等同,稀释了原始问题中「制作」和「双标行为」的核心目的,降低了内容针对性。\n【最终得分 0.30】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.21059999999999998
+    },
+    "sug_猫咪梗图上帝_r2_q13_9": {
+      "type": "sug",
+      "query": "[SUG] 猫咪梗图上帝",
+      "level": 23,
+      "relevance_score": 0.17,
+      "evaluationReason": "【评估对象】词条\"猫咪梗图上帝\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.00】原始问题明确意图是“制作”梗图,而sug词条「猫咪梗图上帝」是一个名词,没有明确的动作意图。因此,sug词条无法提供制作的相关动作,导致动机维度得分低。\n【品类维度 0.25】原始问题问的是“梗图”,sug词条只出现“梗图”,对象层匹配。但原始问题强调“人类双标行为”和“猫咪”这两个重要限定词,sug词条未提及,且“上帝”属于过度泛化和抽象词汇,覆盖度较低。\n【延伸词维度 -0.15】sug词条「上帝」与原始问题「制作反映人类双标行为的猫咪表情包梗图」的核心目的和作用域无关,引入了无关信息,稀释了原始问题的聚焦度,降低了内容的针对性。\n【最终得分 0.17】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.21059999999999998
+    },
+    "q_表情包梗图_r2_14": {
+      "type": "q",
+      "query": "[Q] 表情包梗图",
+      "level": 22,
+      "relevance_score": 0.1755,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": -1,
+      "domain_type": "D3"
+    },
+    "sug_表情包梗图模板_r2_q14_0": {
+      "type": "sug",
+      "query": "[SUG] 表情包梗图模板",
+      "level": 23,
+      "relevance_score": 0.19400000000000003,
+      "evaluationReason": "【评估对象】词条\"表情包梗图模板\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是“制作”表情包梗图,而sug词条「表情包梗图模板」中不包含明确的动作意图。因此,sug词条无法识别动作意图,动机维度得分0分。\n【品类维度 0.28】sug词条仅包含原始问题中最宽泛的“表情包梗图”这一核心对象,缺失『人类双标行为』、『猫咪』等重要的限定词,覆盖度低。\n【延伸词维度 -0.15】原始问题聚焦于「制作反映人类双标行为的猫咪表情包梗图」这一特定主题和内容,而sug词条「模板」则将范围泛化,稀释了原始问题的核心内容和目的,属于作用域稀释型。\n【最终得分 0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.1755
+    },
+    "sug_表情包梗图抽象_r2_q14_1": {
+      "type": "sug",
+      "query": "[SUG] 表情包梗图抽象",
+      "level": 23,
+      "relevance_score": 0.37,
+      "evaluationReason": "【评估对象】词条\"表情包梗图抽象\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题的核心动机是「制作」,而sug词条「表情包梗图抽象」中没有明确的动作意图。\n【品类维度 0.50】原始问题对象层为「猫咪表情包梗图」,场景层为「反映人类双标行为」。Sug词条对象层为「表情包梗图」。对象层命中「表情包梗图」,但其是猫咪表情包梗图的泛化,且缺失所有场景限定词,属于部分匹配。\n【延伸词维度 -0.15】原始问题聚焦于「人类双标行为」和「猫咪表情包梗图」的结合,强调具体内容和形式。「抽象」作为延伸词,与原始问题中的具体内容和形式无关,稀释了原始问题的聚焦度,降低了内容的针对性。\n【最终得分 0.37】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.1755
+    },
+    "sug_jojo表情包梗图_r2_q14_2": {
+      "type": "sug",
+      "query": "[SUG] jojo表情包梗图",
+      "level": 23,
+      "relevance_score": -0.3500000000000001,
+      "evaluationReason": "【评估对象】词条\"jojo表情包梗图\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是「制作」表情包梗图。平台sug词条「jojo表情包梗图」没有明确的动作意图。因此,动机维度得分为0。\n【品类维度 -0.40】原始问题核心对象是「猫咪」「表情包」「梗图」,sug词条只匹配「表情包」和「梗图」,但核心主题「猫咪」被替换为「jojo」,存在主题错位,导致关联度降低。\n【延伸词维度 -0.15】sug词条中的“jojo”是延伸词,它引入了与原始问题“猫咪表情包”完全不相关的动漫主题,稀释了原始问题对“猫咪”和“双标行为”的聚焦,降低了内容的针对性。\n【最终得分 -0.35】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.1755
+    },
+    "sug_表情包梗图学习_r2_q14_3": {
+      "type": "sug",
+      "query": "[SUG] 表情包梗图学习",
+      "level": 23,
+      "relevance_score": 0.39499999999999996,
+      "evaluationReason": "【评估对象】词条\"表情包梗图学习\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.35】原始问题意图是「制作」,sug词条意图是「学习」。学习是制作的前置相关路径或准备,属于弱相关。\n【品类维度 0.50】原始问题对象层为“猫咪表情包梗图”,限定词有“反映人类双标行为”;sug词条对象层为“表情包梗图”,缺失限定词,核心主体匹配,但限定词缺失。\n【延伸词维度 0.00】sug词条未引入延伸词,所有词汇均属于原始问题作用域范围。\n【最终得分 0.39】\n【规则说明】情况4:无延伸词,权重调整为 动机70% + 品类30%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.1755
+    },
+    "sug_表情包梗图动图_r2_q14_4": {
+      "type": "sug",
+      "query": "[SUG] 表情包梗图动图",
+      "level": 23,
+      "relevance_score": 0.41000000000000003,
+      "evaluationReason": "【评估对象】词条\"表情包梗图动图\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题核心动机是“制作表情包”,而sug词条「表情包梗图动图」是一个纯名词短语,无法识别出任何动作意图,因此无法评估动作的匹配度。\n【品类维度 0.50】原始问题核心对象是“猫咪表情包梗图”,sug词为“表情包梗图动图”。sug词包含核心对象,但缺失“猫咪”和“双标行为”的限定,且多了一个“动图”的无关限定。\n【延伸词维度 0.05】原始问题聚焦于制作静态表情包梗图,sug词条中的“动图”是表情包的一种形式,属于辅助型延伸词,对原始问题有辅助作用,但非必需。\n【最终得分 0.41】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.1755
+    },
+    "sug_表情包梗图cp_r2_q14_5": {
+      "type": "sug",
+      "query": "[SUG] 表情包梗图cp",
+      "level": 23,
+      "relevance_score": 0.17,
+      "evaluationReason": "【评估对象】词条\"表情包梗图cp\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包、梗图\n【动机维度 0.00】原始问题是“制作”表情包梗图,sug词条「表情包梗图cp」缺失动作意图,无法判断其动机意图。故动机维度得分0分。\n【品类维度 0.25】原始问题对象层为「表情包梗图」,限定词为「人类双标行为」「猫咪」;sug词条对象层为「表情包梗图」,限定词为「cp」。对象层部分匹配,但sug词条限定词与原始问题完全不匹配,覆盖度低。\n【延伸词维度 -0.15】延伸词“cp”与原始问题中制作“反映人类双标行为的猫咪表情包梗图”的核心目的和作用域无关,引入了新的主题,稀释了原始问题的聚焦度,降低了内容的针对性。\n【最终得分 0.17】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.1755
+    },
+    "sug_重返未来表情包梗图_r2_q14_6": {
+      "type": "sug",
+      "query": "[SUG] 重返未来表情包梗图",
+      "level": 23,
+      "relevance_score": 0.010000000000000009,
+      "evaluationReason": "【评估对象】词条\"重返未来表情包梗图\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题的核心动机是“制作”,sug词条「重返未来表情包梗图」未体现任何明确的动作意图。因此,动机维度匹配度为0。\n【品类维度 0.05】sug词条仅包含原始问题中最宽泛的「表情包梗图」这一对象层,但原始问题的主体「猫咪」及场景层「反映人类双标行为」完全缺失。覆盖度极低,且核心主体完全不对应。\n【延伸词维度 -0.15】sug词条中的“重返未来”与原始问题中的“人类双标行为的猫咪表情包梗图”主题完全不符,引入了无关信息,稀释了原始问题的聚焦度,降低了内容针对性。\n【最终得分 0.01】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.1755
+    },
+    "sug_表情包梗图可爱_r2_q14_7": {
+      "type": "sug",
+      "query": "[SUG] 表情包梗图可爱",
+      "level": 23,
+      "relevance_score": 0.034,
+      "evaluationReason": "【评估对象】词条\"表情包梗图可爱\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题的核心动机是“制作”。sug词条“表情包梗图可爱”缺乏明确的动作意图。因此,sug词条无法与原始问题的核心动机匹配。\n【品类维度 0.08】sug词是通用概念“表情包梗图”,原始问题是特定主题“反映人类双标行为的猫咪表情包梗图”。sug词过度泛化,未包含核心对象“猫咪”和限定词。\n【延伸词维度 -0.15】原始问题聚焦于「人类双标行为」和「猫咪表情包梗图」的结合,强调内容创作。sug词条「可爱」引入了与原始问题核心目的无关的审美维度,稀释了对「双标行为」这一主题的关注,降低了内容的针对性。\n【最终得分 0.03】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.1755
+    },
+    "sug_表情包梗图恶俗_r2_q14_8": {
+      "type": "sug",
+      "query": "[SUG] 表情包梗图恶俗",
+      "level": 23,
+      "relevance_score": -0.23,
+      "evaluationReason": "【评估对象】词条\"表情包梗图恶俗\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的动机是「制作」表情包梗图,而sug词条中没有明确的动作意图。sug词条仅提及了表情包梗图的属性/评价,与原始问题的制作动机不匹配。\n【品类维度 -0.25】原始问题是关于“制作反映人类双标行为的猫咪表情包梗图”,sug词条“表情包梗图恶俗”虽有主体词“表情包梗图”,但增加了负面评价限定词“恶俗”,与原始问题的中性或偏创作意图不符,存在负向偏离。\n【延伸词维度 -0.15】延伸词“恶俗”与原始问题“制作反映人类双标行为的猫咪表情包梗图”的核心目的和作用域无关,反而引入了负面评价,稀释了用户对制作方法的关注,属于作用域稀释型。\n【最终得分 -0.23】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.1755
+    },
+    "sug_表情包梗图开心_r2_q14_9": {
+      "type": "sug",
+      "query": "[SUG] 表情包梗图开心",
+      "level": 23,
+      "relevance_score": 0.034,
+      "evaluationReason": "【评估对象】词条\"表情包梗图开心\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是「制作」表情包梗图,而sug词条「表情包梗图开心」没有明确的动作意图。因此,sug词条未能体现原始问题的制作动机。\n【品类维度 0.08】原始问题需求是制作「反映人类双标行为的猫咪表情包梗图」,对象层是「猫咪表情包梗图」。sug词条只包含「表情包梗图」这个过度泛化的概念,缺失了「猫咪」这个核心限定词,且增加了无关的「开心」限定词,覆盖度低。\n【延伸词维度 -0.15】延伸词“开心”与原始问题“制作反映人类双标行为的猫咪表情包梗图”的核心目的和作用域无关,反而稀释了原始问题中“双标行为”这一特定主题的聚焦度,降低了内容的针对性。\n【最终得分 0.03】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.1755
+    },
+    "q_反映人类双标_r2_15": {
+      "type": "q",
+      "query": "[Q] 反映人类双标",
+      "level": 22,
+      "relevance_score": 0.0882,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": -1,
+      "domain_type": "D2"
+    },
+    "sug_反映人类双胞胎基因变异_r2_q15_0": {
+      "type": "sug",
+      "query": "[SUG] 反映人类双胞胎基因变异",
+      "level": 23,
+      "relevance_score": -0.8,
+      "evaluationReason": "【评估对象】词条\"反映人类双胞胎基因变异\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.00】原始问题的核心动机是“制作表情包”,而sug词条中无明确的动作意图,因此无法匹配。\n【品类维度 -0.85】原始问题对象层为「猫咪表情包梗图」,场景层为「人类双标行为」。sug词条「人类双胞胎基因变异」在对象和场景上均与原始问题完全不匹配,是完全错误的品类。\n【延伸词维度 -0.60】sug词条中的“双胞胎基因变异”与原始问题中的“双标行为”在语义上完全不符,且“双胞胎基因变异”与“猫咪表情包梗图”也无关联,严重偏离了原始问题的核心主题和目的,属于作用域稀释型延伸词,且程度较深。\n【最终得分 -0.80】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.0882
+    },
+    "q_反映人类双标行为_r2_16": {
+      "type": "q",
+      "query": "[Q] 反映人类双标行为",
+      "level": 22,
+      "relevance_score": 0.0882,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": -1,
+      "domain_type": "D2"
+    },
+    "sug_反映人类双标行为的图片_r2_q16_0": {
+      "type": "sug",
+      "query": "[SUG] 反映人类双标行为的图片",
+      "level": 23,
+      "relevance_score": 0.44000000000000006,
+      "evaluationReason": "【评估对象】词条\"反映人类双标行为的图片\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.00】sug词条「反映人类双标行为的图片」无明确动作意图,只提到了一个对象,未能体现原始问题中的核心动作「制作」。\n【品类维度 0.55】原始问题需求是「猫咪表情包梗图」,sug词条是「图片」。虽然sug覆盖了「反映人类双标行为」的场景限定,但核心对象「猫咪表情包梗图」与「图片」相比过于泛化,导致匹配度下降。\n【延伸词维度 0.00】sug词条未引入延伸词,所有词汇均属于原始问题作用域范围。原始问题中的“猫咪表情包梗图”被sug词条简化为“图片”,这属于原始问题对象层的泛化,不构成延伸词。\n【最终得分 0.44】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.0882
+    },
+    "q_人类双标行为_r2_17": {
+      "type": "q",
+      "query": "[Q] 人类双标行为",
+      "level": 22,
+      "relevance_score": 0.081,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": -1,
+      "domain_type": "D2"
+    },
+    "sug_人类双标行为是什么意思_r2_q17_0": {
+      "type": "sug",
+      "query": "[SUG] 人类双标行为是什么意思",
+      "level": 23,
+      "relevance_score": -0.09500000000000001,
+      "evaluationReason": "【评估对象】词条\"人类双标行为是什么意思\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.00】原始问题的核心动机是「制作」表情包梗图,而sug词条「人类双标行为是什么意思」的动机是「了解/理解」一个概念。两者动机方向完全不一致。\n【品类维度 -0.20】原始问题主对象是“猫咪表情包梗图”,限定词是“反映人类双标行为”;sug词是“人类双标行为是什么意思”,主对象是“人类双标行为”,两者完全不匹配,sug词的限定词甚至在原始问题中是修饰成分。\n【延伸词维度 -0.15】延伸词“是什么意思”将原始问题从“制作”行为转变为“理解”概念,稀释了原始问题中“制作梗图”的核心目的,降低了内容针对性。\n【最终得分 -0.10】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.081
+    },
+    "q_双标行为_r2_18": {
+      "type": "q",
+      "query": "[Q] 双标行为",
+      "level": 22,
+      "relevance_score": 0.0765,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": -1,
+      "domain_type": "D2"
+    },
+    "sug_双标行为举例_r2_q18_0": {
+      "type": "sug",
+      "query": "[SUG] 双标行为举例",
+      "level": 23,
+      "relevance_score": 0.21,
+      "evaluationReason": "【评估对象】词条\"双标行为举例\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是「制作」表情包梗图,而sug词条「双标行为举例」中没有明确的动作意图。\n【品类维度 0.30】原始问题核心是「猫咪表情包梗图」,限定词是「人类双标行为」。Sug词条仅包含「双标行为」,与原始问题的限定词部分重叠,但核心对象「猫咪表情包梗图」完全缺失,导致匹配度较低。\n【延伸词维度 -0.15】sug词条「双标行为举例」与原始问题「制作反映人类双标行为的猫咪表情包梗图」的核心目的不符。原始问题侧重于“制作”和“猫咪表情包梗图”,而sug词条仅提供了“双标行为举例”,这属于原始问题中“双标行为”的背景知识,而非其核心动作或对象。它稀释了原始问题中“制作”和“表情包”的聚焦度,降低了内容针对性,属于作用域稀释型。\n【最终得分 0.21】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.0765
+    },
+    "sug_双标行为内涵是什么_r2_q18_1": {
+      "type": "sug",
+      "query": "[SUG] 双标行为内涵是什么",
+      "level": 23,
+      "relevance_score": -0.23,
+      "evaluationReason": "【评估对象】词条\"双标行为内涵是什么\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】原始问题意图是「制作」反映人类双标行为的猫咪表情包梗图,sug词条「双标行为内涵是什么」意图是「了解」双标行为的内涵,两者动词不同,且sug词条无明确动机。\n【品类维度 -0.25】原始问题对象层包括“猫咪表情包梗图”和“人类双标行为”,场景层未明确。Sug词条对象层为“双标行为”,场景层不明确。原始问题询问制作方法而sug词条只涉及对“双标行为”的定义,品类不匹配且缺失核心对象“猫咪表情包梗图”。\n【延伸词维度 -0.15】原始问题聚焦于「制作」猫咪表情包梗图,而sug词条「双标行为内涵是什么」将主题从「制作」转移到「概念解释」,稀释了原始问题的创作目的,属于作用域稀释型。\n【最终得分 -0.23】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.0765
+    },
+    "sug_讽刺双标的文案_r2_q18_2": {
+      "type": "sug",
+      "query": "[SUG] 讽刺双标的文案",
+      "level": 23,
+      "relevance_score": 0.010000000000000009,
+      "evaluationReason": "【评估对象】词条\"讽刺双标的文案\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作/生成(梗图)\n【动机维度 -0.15】原始问题的核心动机是「制作(梗图)」,sug词条的动机是「撰写/找寻(文案)」。两者动机存在部分重合,但方向上制作(图像)与撰写(文字)存在明显偏差。\n【品类维度 0.25】原始问题对象层为「猫咪表情包梗图」,场景层为「反映人类双标行为」。Sug词条对象层为「文案」,场景层为「讽刺双标」。场景层有部分匹配,但核心对象层「猫咪表情包梗图」与「文案」完全不符。\n【延伸词维度 -0.15】原始问题聚焦于「制作」猫咪表情包梗图,延伸词「文案」虽然与「讽刺双标」相关,但偏离了「制作」和「猫咪表情包」的核心对象,引入了不相关的创作形式,稀释了原始问题的聚焦度。\n【最终得分 0.01】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.0765
+    },
+    "sug_王楚钦双标行为_r2_q18_3": {
+      "type": "sug",
+      "query": "[SUG] 王楚钦双标行为",
+      "level": 23,
+      "relevance_score": -0.7600000000000001,
+      "evaluationReason": "【评估对象】词条\"王楚钦双标行为\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是「制作」表情包梗图。sug词条「王楚钦双标行为」仅是一个话题,不包含任何动作意图。因此,sug词条无法匹配原始问题的动机。\n【品类维度 -0.80】原始问题核心对象为「猫咪表情包梗图」与「人类双标行为」,强调「制作」。sug词条「王楚钦双标行为」对象为特定人物的「双标行为」。两者核心对象和限定词完全不匹配,品类冲突,属于负向偏离。\n【延伸词维度 -0.60】sug词条中的“王楚钦”和“双标行为”均是延伸词。“王楚钦”引入了与原始问题完全无关的人物,且“双标行为”脱离了原始问题中“猫咪表情包梗图”的创作语境,导致sug词条与原始问题核心目的完全偏离,严重稀释了原始问题的聚焦度。\n【最终得分 -0.76】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.0765
+    },
+    "sug_猫咪为什么会出现双标行为_r2_q18_4": {
+      "type": "sug",
+      "query": "[SUG] 猫咪为什么会出现双标行为",
+      "level": 23,
+      "relevance_score": -0.019999999999999997,
+      "evaluationReason": "【评估对象】词条\"猫咪为什么会出现双标行为\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 -0.05】原始问题意图是「制作」表情包,sug词条是「探讨/理解」猫咪行为原因。两者意图方向不同,原始是创作行为,sug是知识探索行为,尽管主题相关,但动作意图相悖。\n【品类维度 0.05】原始问题核心对象是“表情包梗图”和“双标行为”,场景是“猫咪”。sug词条仅包含“猫咪”和“双标行为”这两个场景与主体,但它是一个现象的提问,而不是制作过程。内容,不匹配“表情包梗图”这个核心对象,因此匹配度低,但也不算有关联结到了一部分主体,是抽象相似。\n【延伸词维度 -0.15】原始问题是关于「制作」表情包梗图,核心是创作行为。sug词条「猫咪为什么会出现双标行为」将主题从创作转向了对猫咪行为的「解释」,引入了与原始问题核心目的无关的知识性探究,稀释了原始问题的聚焦度。\n【最终得分 -0.02】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.0765
+    },
+    "sug_双标男友的典型表现_r2_q18_5": {
+      "type": "sug",
+      "query": "[SUG] 双标男友的典型表现",
+      "level": 23,
+      "relevance_score": -0.43000000000000005,
+      "evaluationReason": "【评估对象】词条\"双标男友的典型表现\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题意图是「制作」表情包梗图,而sug词条「双标男友的典型表现」并无明确动作意图,因此无法进行动作匹配,动机维度评分为0。\n【品类维度 -0.50】原始问题内容主体为《人类双标行为的猫咪表情包梗图》,sug词条内容主体为《双标男友的典型表现》。两者核心对象和限定词完全不匹配,品类完全冲突。\n【延伸词维度 -0.15】sug词条「双标男友」与原始问题中的「人类双标行为」在概念上有所关联,但将范围限定在「男友」这一特定人群,且未提及「猫咪表情包梗图」这一核心对象,导致原始问题的核心目的被稀释。\n【最终得分 -0.43】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.0765
+    },
+    "sug_猫咪双标行为搞笑视频_r2_q18_6": {
+      "type": "sug",
+      "query": "[SUG] 猫咪双标行为搞笑视频",
+      "level": 23,
+      "relevance_score": 0.33,
+      "evaluationReason": "【评估对象】词条\"猫咪双标行为搞笑视频\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包/梗图\n【动机维度 0.25】原始问题核心动机是“制作”梗图,而sug词条是“视频”,两者形式不同且sug词条未体现制作意图。虽然都是内容创作(制作/拍摄),但sug词条未明确动作意图,因此匹配度较低。\n【品类维度 0.55】原始问题对象层为「猫咪表情包梗图」,场景层包括「人类双标行为」。sug词条对象层为「猫咪视频」,场景层包括「猫咪双标行为」「搞笑」。场景层「猫咪双标行为」与原始问题「人类双标行为」核心概念相似,但对象层「视频」与「表情包梗图」不符,有部分重叠。故评分为0.55。\n【延伸词维度 -0.15】原始问题聚焦于「表情包梗图」的制作,而sug词条引入了「搞笑视频」这一新的内容形式。这稀释了原始问题的核心目的,从制作静态图片转向了动态视频,降低了内容的针对性。\n【最终得分 0.33】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.0765
+    },
+    "sug_猫咪双标行为_r2_q18_7": {
+      "type": "sug",
+      "query": "[SUG] 猫咪双标行为",
+      "level": 23,
+      "relevance_score": 0.44000000000000006,
+      "evaluationReason": "【评估对象】词条\"猫咪双标行为\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作,意图是产出表情包梗图\n【动机维度 0.00】原始问题明确意图是“制作”表情包梗图,而sug词条「猫咪双标行为」仅提供了制作的对象和主题,没有体现任何动作意图,因此动机维度评分为0。\n【品类维度 0.55】sug词条「猫咪双标行为」与原始问题「人类双标行为的猫咪表情包梗图」在对象层都包含「猫咪」和「双标行为」。原始问题还包含「表情包梗图」的核心对象和「人类」限定词,sug词条未涵盖。存在部分主体匹配和限定词匹配,但核心信息欠缺。\n【延伸词维度 0.00】sug词条未引入延伸词,所有词汇均属于原始问题作用域范围。\n【最终得分 0.44】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#22c55e",
+      "parentQScore": 0.0765
+    },
+    "sug_双标是什么意思啊_r2_q18_8": {
+      "type": "sug",
+      "query": "[SUG] 双标是什么意思啊",
+      "level": 23,
+      "relevance_score": 0.0050000000000000044,
+      "evaluationReason": "【评估对象】词条\"双标是什么意思啊\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作梗图,表达双标行为\n【动机维度 0.00】原始问题的核心动机是「制作」梗图,而sug词条「双标是什么意思啊」是「理解」或「查询」概念。sug词条与原始问题的制作动机完全不匹配。\n【品类维度 0.05】原始问题需求是制作猫咪双标行为表情包梗图,对象层有「双标行为」和「猫咪表情包梗图」。sug词条只涉及了「双标」这一概念,对象层和场景层覆盖度极低。\n【延伸词维度 -0.15】sug词条「双标是什么意思啊」与原始问题「制作反映人类双标行为的猫咪表情包梗图」的核心目的和作用域完全不符。原始问题是关于创作和制作,而sug词条是关于概念解释,引入了无关信息,稀释了原始问题的聚焦度,降低了内容的针对性。\n【最终得分 0.01】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.0765
+    },
+    "sug_双标的人怎么怼_r2_q18_9": {
+      "type": "sug",
+      "query": "[SUG] 双标的人怎么怼",
+      "level": 23,
+      "relevance_score": -0.17500000000000004,
+      "evaluationReason": "【评估对象】词条\"双标的人怎么怼\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题意图是「制作」表情包梗图,而sug词条「怎么怼」意图是「反击/对抗」。两者动作意图完全不匹配。\n【品类维度 -0.40】原始问题内容主体为「人类双标行为」和「猫咪表情包梗图」,sug词条内容主体为「双标的人」。sug只有「双标」相关概念,缺少「猫咪表情包梗图」这一核心对象,且sug词侧重于互动反驳,与原始问题制作内容的主体含义有较大差异,存在品类错位。\n【延伸词维度 -0.15】sug词条「双标的人怎么怼」中的“怼”是延伸词,它引入了与原始问题“制作表情包梗图”完全不相关的行为,稀释了原始问题的创作和表达目的,偏离了核心需求。\n【最终得分 -0.18】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.0765
+    },
+    "q_反映人类_r2_19": {
+      "type": "q",
+      "query": "[Q] 反映人类",
+      "level": 22,
+      "relevance_score": 0.0702,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": -1,
+      "domain_type": "D2"
+    },
+    "sug_反映人类社会发展进步的价值理念_r2_q19_0": {
+      "type": "sug",
+      "query": "[SUG] 反映人类社会发展进步的价值理念",
+      "level": 23,
+      "relevance_score": -0.7600000000000001,
+      "evaluationReason": "【评估对象】词条\"反映人类社会发展进步的价值理念\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是“制作”,而sug词条「反映人类社会发展进步的价值理念」中没有明确的动作意图。sug词条无动机,因此动机维度评分为0。\n【品类维度 -0.80】原始问题核心对象是《猫咪表情包梗图》,限定词是《人类双标行为》。sug词条核心对象是《价值理念》,限定词是《人类社会发展进步》,两者在对象和限定词上均无任何匹配,品类完全冲突。\n【延伸词维度 -0.60】sug词条中的“社会发展进步的价值理念”与原始问题中的“人类双标行为的猫咪表情包梗图”完全不相关,引入了与原始问题核心目的和作用域完全无关的宏大主题,严重稀释了原始问题的聚焦度,导致内容偏离。\n【最终得分 -0.76】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.0702
+    },
+    "sug_人类图反映者_r2_q19_1": {
+      "type": "sug",
+      "query": "[SUG] 人类图反映者",
+      "level": 23,
+      "relevance_score": -0.19000000000000003,
+      "evaluationReason": "【评估对象】词条\"人类图反映者\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是“制作”表情包梗图。sug词条「人类图反映者」不包含任何动作意图,因此无法与原始问题的动作意图进行匹配。\n【品类维度 -0.20】原始问题核心对象是“猫咪表情包梗图”和“双标行为”,场景限定“人类”。Sug词是“人类图反映者”,对象完全不匹配,不具备相关性,存在误导性。\n【延伸词维度 -0.15】sug词条「人类图反映者」与原始问题「制作反映人类双标行为的猫咪表情包梗图」的核心目的和对象完全不符,引入了无关概念,稀释了原始问题的聚焦度。\n【最终得分 -0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.0702
+    },
+    "sug_怎么识别人类图中的反映者_r2_q19_2": {
+      "type": "sug",
+      "query": "[SUG] 怎么识别人类图中的反映者",
+      "level": 23,
+      "relevance_score": -0.09500000000000001,
+      "evaluationReason": "【评估对象】词条\"怎么识别人类图中的反映者\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图),内容是反映双标行为\n【动机维度 0.00】原始问题的核心动机是《制作》表情包梗图,而sug词条的核心动机是《识别》人类图像中的反映者。两者动机完全不匹配,无任何关联。\n【品类维度 -0.20】原始问题核心是「猫咪表情包梗图」这一对象和「人类双标行为」这一场景限定。sug词条「识别人类图中的反映者」与原始问题对象层和场景层均不匹配,二者核心主体完全不同,存在品类错位。\n【延伸词维度 -0.15】sug词条中的“识别人类图中的反映者”与原始问题“制作反映人类双标行为的猫咪表情包梗图”的核心目的和作用域完全不符,引入了无关的识别概念,稀释了原始问题关于“制作”和“猫咪表情包”的聚焦度,属于作用域稀释型。\n【最终得分 -0.10】",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.0702
+    },
+    "sug_人类图显示者_r2_q19_3": {
+      "type": "sug",
+      "query": "[SUG] 人类图显示者",
+      "level": 23,
+      "relevance_score": -0.52,
+      "evaluationReason": "【评估对象】词条\"人类图显示者\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题意图是「制作」表情包梗图,而sug词条「人类图显示者」无法识别出任何动作意图,因此动机匹配度为0。\n【品类维度 -0.50】原始问题核心对象是「猫咪表情包梗图」及「人类双标行为」内容。Sug词「人类图显示者」是完全不相关的概念。核心对象和内容主体均完全不匹配,品类冲突。\n【延伸词维度 -0.60】sug词条「人类图显示者」与原始问题「制作反映人类双标行为的猫咪表情包梗图」完全不相关,属于作用域无关型,且严重偏离原始问题,稀释了原始问题的核心目的。\n【最终得分 -0.52】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.0702
+    },
+    "sug_人类图解读_r2_q19_4": {
+      "type": "sug",
+      "query": "[SUG] 人类图解读",
+      "level": 23,
+      "relevance_score": -0.8,
+      "evaluationReason": "【评估对象】词条\"人类图解读\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包\n【动机维度 0.00】原始问题核心动机是「制作」表情包,而Sug词条「人类图解读」不包含任何明确的动机,因此无法匹配。\n【品类维度 -0.85】原始问题意在生成「猫咪表情包梗图」,对象和场景为「猫咪」和「表情包梗图」。sug词为「人类图解读」,与原始问题中的核心主体「猫咪」和「表情包梗图」完全不匹配,是完全不同的概念类别,属于负向偏离。\n【延伸词维度 -0.60】原始问题聚焦于制作「猫咪表情包梗图」以反映「人类双标行为」,核心是内容创作和主题表达。「人类图解读」是一个完全不相关的概念,与原始问题的动机、对象、场景均无关联,属于严重的稀释型延伸词,将用户引导至完全不同的领域,对原始问题目的达成造成负面影响。\n【最终得分 -0.80】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.0702
+    },
+    "sug_人类图是什么_r2_q19_5": {
+      "type": "sug",
+      "query": "[SUG] 人类图是什么",
+      "level": 23,
+      "relevance_score": -0.38000000000000006,
+      "evaluationReason": "【评估对象】词条\"人类图是什么\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.00】原始问题动机是“制作表情包”,sug词条“人类图是什么”的动机是“了解/学习”某个概念,两者动作意图完全不匹配。\n【品类维度 -0.80】原始问题核心对象是《人类双标行为的猫咪表情包梗图》,场景限定涉及《人类双标行为》。sug词条《人类图是什么》的核心对象是《人类图》,两者在品类上完全不符,主体词和限定词均无关联。\n【延伸词维度 -0.60】sug词条「人类图」与原始问题「制作反映人类双标行为的猫咪表情包梗图」在主题和目的上完全不相关,引入了与原始问题核心需求无关的全新概念,严重稀释了原始问题的聚焦度,导致内容偏离,属于作用域稀释型。\n【最终得分 -0.38】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.0702
+    },
+    "sug_反映者适合什么工作_r2_q19_6": {
+      "type": "sug",
+      "query": "[SUG] 反映者适合什么工作",
+      "level": 23,
+      "relevance_score": -0.38000000000000006,
+      "evaluationReason": "【评估对象】词条\"反映者适合什么工作\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.00】原始问题的核心动机是\"制作\"表情包梗图。sug词条「反映者适合什么工作」的动机是\"适合\"或\"寻找\"工作。两者动作完全不匹配且无关。\n【品类维度 -0.80】原始问题核心是「人类双标行为」「猫咪表情包梗图」,sug词条是「反映者适合什么工作」。两者对象层和场景层均完全不匹配,是完全不同的话题领域。\n【延伸词维度 -0.60】sug词条「反映者适合什么工作」与原始问题「如何制作反映人类双标行为的猫咪表情包梗图」完全不相关,引入了与原始问题制作表情包梗图完全无关的职业选择话题,严重稀释了原始问题的聚焦度。\n【最终得分 -0.38】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.0702
+    },
+    "sug_人类图投射者_r2_q19_7": {
+      "type": "sug",
+      "query": "[SUG] 人类图投射者",
+      "level": 23,
+      "relevance_score": -0.7100000000000001,
+      "evaluationReason": "【评估对象】词条\"人类图投射者\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包\n【动机维度 0.00】原始问题意图是「制作」表情包,sug词条「人类图投射者」仅为名词短语,不包含任何动作意图,因此无法评估动作匹配度。\n【品类维度 -0.85】原始问题核心是制作「猫咪表情包梗图」,限定词为「反映人类双标行为」。sug词是「人类图投射者」,两者在核心对象和限定词上完全不匹配,品类差异巨大。\n【延伸词维度 -0.15】sug词条「人类图投射者」与原始问题「制作反映人类双标行为的猫咪表情包梗图」的核心目的和作用域完全不符,引入了无关概念,稀释了原始问题的聚焦度。\n【最终得分 -0.71】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.0702
+    },
+    "sug_反映者的代表人物_r2_q19_8": {
+      "type": "sug",
+      "query": "[SUG] 反映者的代表人物",
+      "level": 23,
+      "relevance_score": -0.19000000000000003,
+      "evaluationReason": "【评估对象】词条\"反映者的代表人物\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包、梗图),表达/展现(反映人类双标行为)\n【动机维度 0.00】原始问题的核心动机是“制作”猫咪梗图以“反映”人类行为。sug词条「反映者的代表人物」中,动词“反映”与原始问题中的“反映”存在字面重叠,但sug词条没有明确的动机意图,因此无法评估动机匹配度。\n【品类维度 -0.20】原始问题核心对象是《猫咪表情包梗图》,限定词包括《人类双标行为》、《制作》。sug词《反映者的代表人物》与原始问题对象与限定词完全不符,品类错位。\n【延伸词维度 -0.15】原始问题聚焦于「制作」猫咪表情包梗图,而sug词条「反映者的代表人物」引入了与制作行为无关的人物概念,稀释了原始问题的核心目的,属于作用域稀释型。\n【最终得分 -0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.0702
+    },
+    "sug_人类图测试_r2_q19_9": {
+      "type": "sug",
+      "query": "[SUG] 人类图测试",
+      "level": 23,
+      "relevance_score": -0.3350000000000001,
+      "evaluationReason": "【评估对象】词条\"人类图测试\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作/生成(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是\"制作/生成\",而sug词条\"人类图测试\"的动机是\"测试\"。二者动机没有相关性。\n【品类维度 -0.80】原始问题集中在「猫咪表情包梗图」的制作,兼含「人类双标行为」限定。sug词条「人类图测试」与核心主体「表情包梗图」和限定词均无关联,品类严重不匹配。\n【延伸词维度 -0.15】原始问题是关于制作猫咪表情包梗图以反映人类双标行为,核心是「制作」和「猫咪表情包梗图」。sug词条「人类图测试」引入了与原始问题完全不相关的概念「人类图测试」,既不属于原始问题的任何作用域,也无助于原始目的的达成,反而稀释了原始问题的聚焦度。\n【最终得分 -0.34】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.0702
+    },
+    "q_反映人类行为_r2_20": {
+      "type": "q",
+      "query": "[Q] 反映人类行为",
+      "level": 22,
+      "relevance_score": 0.0702,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": -1,
+      "domain_type": "D2"
+    },
+    "sug_反映人类行为的环境问题_r2_q20_0": {
+      "type": "sug",
+      "query": "[SUG] 反映人类行为的环境问题",
+      "level": 23,
+      "relevance_score": -0.27599999999999997,
+      "evaluationReason": "【评估对象】词条\"反映人类行为的环境问题\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.00】sug词条缺乏明确的动作意图和目的性,无法评估动作匹配度。原始问题的核心动作是「制作」,而sug词条没有表现出任何动作意图。\n【品类维度 -0.30】原始问题核心对象为「猫咪表情包梗图」,限定词为「人类双标行为」。sug词条核心对象为「环境问题」,限定词为「人类行为」,两者对象不匹配且限定词差异大,存在品类冲突。\n【延伸词维度 -0.18】sug词条中的“环境问题”是延伸词,它与原始问题中的“人类双标行为”和“猫咪表情包梗图”完全不相关,严重稀释了原始问题的聚焦度,导致内容偏离核心。\n【最终得分 -0.28】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.0702
+    },
+    "q_人类行为_r2_21": {
+      "type": "q",
+      "query": "[Q] 人类行为",
+      "level": 22,
+      "relevance_score": 0.045,
+      "evaluationReason": "",
+      "strategy": "Query",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "domain_index": -1,
+      "domain_type": "D2"
+    },
+    "sug_人类行为与社会环境_r2_q21_0": {
+      "type": "sug",
+      "query": "[SUG] 人类行为与社会环境",
+      "level": 23,
+      "relevance_score": -0.19000000000000003,
+      "evaluationReason": "【评估对象】词条\"人类行为与社会环境\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.00】sug词条「人类行为与社会环境」是纯名词性短语,无法识别出任何明确的动作意图。因此,无法与原始问题「制作」的动作意图进行匹配。\n【品类维度 -0.20】原始问题核心对象是《猫咪表情包梗图》,限定词是《人类双标行为》。sug词《人类行为与社会环境》只在《人类行为》上与原始问题概念有弱关联,但sug词本身过于泛化且缺少核心对象和场景,无法匹配。\n【延伸词维度 -0.15】原始问题聚焦于「制作猫咪表情包梗图」这一具体行为,并限定了主题「反映人类双标行为」。sug词条「人类行为与社会环境」是高度概括的学术概念,与原始问题的具体制作需求和娱乐属性不符,稀释了原始问题的聚焦度,使其偏离了核心目的。\n【最终得分 -0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.045
+    },
+    "sug_人类行为设计师-小周_r2_q21_1": {
+      "type": "sug",
+      "query": "[SUG] 人类行为设计师-小周",
+      "level": 23,
+      "relevance_score": -0.19000000000000003,
+      "evaluationReason": "【评估对象】词条\"人类行为设计师-小周\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题意图是「制作」表情包梗图,sug词条「人类行为设计师-小周」并未包含动作意图,因此无法匹配。\n【品类维度 -0.20】原始问题内容主题是“猫咪表情包梗图”,限定词是“反映人类双标行为”;sug词条是“人类行为设计师-小周”,内容主题和限定词均与原始问题无关且无关,仅有「人类行为」弱关联,但sug是人物而sug是职业主体词错位\n【延伸词维度 -0.15】sug词条「人类行为设计师-小周」与原始问题「如何制作反映人类双标行为的猫咪表情包梗图」的核心目的和作用域完全不符。它引入了一个与表情包制作、猫咪、双标行为等核心概念无关的职业和人名,严重稀释了原始问题的聚焦度,导致内容偏离。\n【最终得分 -0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.045
+    },
+    "sug_人类行为与社会环境重点_r2_q21_2": {
+      "type": "sug",
+      "query": "[SUG] 人类行为与社会环境重点",
+      "level": 23,
+      "relevance_score": -0.19000000000000003,
+      "evaluationReason": "【评估对象】词条\"人类行为与社会环境重点\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.00】原始问题的核心动机是「制作」表情包梗图。sug词条「人类行为与社会环境重点」仅是名词短语,无任何动作意图,因此动机匹配度为0。\n【品类维度 -0.20】原始问题涉及「猫咪表情包梗图」这一对象,限定词是「人类双标行为」。sug词主对象是「人类行为与社会环境」,与原始问题的主要对象「表情包梗图」完全不符,但二者都含有「人类行为」这一概念,sug词的限定词「社会环境重点」完全不匹配,与原始问题不相关,有一定偏离\n【延伸词维度 -0.15】原始问题聚焦于「制作猫咪表情包梗图」以「反映人类双标行为」,而sug词条「人类行为与社会环境重点」则是一个宽泛的学术领域,与制作表情包这一具体操作和猫咪主题完全无关,属于作用域稀释型延伸词,严重偏离了原始问题的核心目的和对象。\n【最终得分 -0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.045
+    },
+    "sug_人类行为与社会环境笔记_r2_q21_3": {
+      "type": "sug",
+      "query": "[SUG] 人类行为与社会环境笔记",
+      "level": 23,
+      "relevance_score": -0.7100000000000001,
+      "evaluationReason": "【评估对象】词条\"人类行为与社会环境笔记\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作(表情包梗图)\n【动机维度 0.00】原始问题的核心动机是【制作】表情包梗图,sug词条「人类行为与社会环境笔记」无明确的动作意图。\n【品类维度 -0.85】原始问题核心对象是《猫咪表情包梗图》,限定词有《人类双标行为》。sug词条核心对象是《人类行为与社会环境笔记》,与原始问题的核心对象完全不符,品类严重冲突。\n【延伸词维度 -0.15】sug词条「人类行为与社会环境笔记」中的“笔记”是延伸词,它与原始问题「如何制作反映人类双标行为的猫咪表情包梗图」中的“制作”和“梗图”无关,稀释了原始问题的聚焦度,降低了内容针对性。\n【最终得分 -0.71】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.045
+    },
+    "sug_人类行为矫正教育漫画_r2_q21_4": {
+      "type": "sug",
+      "query": "[SUG] 人类行为矫正教育漫画",
+      "level": 23,
+      "relevance_score": -0.21500000000000002,
+      "evaluationReason": "【评估对象】词条\"人类行为矫正教育漫画\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.00】原始问题的核心动机是「制作」表情包梗图,而sug词条「人类行为矫正教育漫画」的动机是「矫正」和「教育」。两者动机意图完全不匹配。\n【品类维度 -0.50】原始问题核心对象是“猫咪表情包梗图”,限定词是“人类双标行为”。Sug词条是“人类行为矫正教育漫画”,其核心对象和限定词与原始问题完全不匹配,品类冲突严重。\n【延伸词维度 -0.15】原始问题聚焦于「制作猫咪表情包梗图」以反映「人类双标行为」,核心是内容创作和主题表达。「人类行为矫正教育漫画」中的「矫正教育」和「漫画」与原始问题的「制作表情包梗图」和「反映双标行为」存在较大偏差,稀释了原始问题的创作性和娱乐性,引入了教育和矫正的无关维度。\n【最终得分 -0.22】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.045
+    },
+    "sug_人类行为与社会环境第三版_r2_q21_5": {
+      "type": "sug",
+      "query": "[SUG] 人类行为与社会环境第三版",
+      "level": 23,
+      "relevance_score": -0.7600000000000001,
+      "evaluationReason": "【评估对象】词条\"人类行为与社会环境第三版\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】原始问题的核心动机是\"制作\"表情包梗图,表达一种创造和产出的意图。它涉及到创作过程中的技能运用和结果产出。\n【动机维度 0.00】原始问题的动机是创造和产出(制作),而sug词条无法识别出明确的动作意图(名词性词条),因此无法进行动作上的匹配。\n【品类维度 -0.80】原始问题核心对象是“猫咪表情包梗图”和“双标行为”,场景限定为“人类”。sug词条是“人类行为与社会环境第三版”,属于学术书籍,对象和场景都与原始问题完全不匹配,是明显错误的品类。\n【延伸词维度 -0.60】sug词条「人类行为与社会环境第三版」与原始问题「制作猫咪表情包梗图」完全不相关,属于作用域无关型延伸词,且严重偏离原始问题核心,稀释了用户意图。\n【最终得分 -0.76】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20% + 规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.045
+    },
+    "sug_人类行为矫正教育_r2_q21_6": {
+      "type": "sug",
+      "query": "[SUG] 人类行为矫正教育",
+      "level": 23,
+      "relevance_score": -0.7350000000000001,
+      "evaluationReason": "【评估对象】词条\"人类行为矫正教育\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 -0.80】原始问题的核心动机是“制作”特定主题(反映人类双标行为的猫咪表情包梗图),而sug词条「人类行为矫正教育」的动机是“教育”或“矫正”。两者动作意图完全不匹配,甚至方向相反,因此得分较低。\n【品类维度 -0.80】原始问题核心对象为「猫咪表情包梗图」,限定词为「反映人类双标行为」。sug词为「人类行为矫正教育」。两者对象、限定词均无匹配,品类完全不同且偏离严重。\n【延伸词维度 -0.15】sug词条「人类行为矫正教育」与原始问题「制作猫咪表情包梗图」的核心目的完全不符,引入了与原始问题无关的全新主题,严重稀释了原始问题的聚焦度,导致内容偏离。\n【最终得分 -0.74】\n【规则说明】规则3:核心维度严重负向,上限=0",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.045
+    },
+    "sug_人类行为心理学_r2_q21_7": {
+      "type": "sug",
+      "query": "[SUG] 人类行为心理学",
+      "level": 23,
+      "relevance_score": 0.010000000000000009,
+      "evaluationReason": "【评估对象】词条\"人类行为心理学\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包\n【动机维度 0.00】原始问题的核心动机是「制作」表情包,而Sug词条「人类行为心理学」无明确的动作意图。\n【品类维度 0.05】原始问题核心是制作《猫咪表情包梗图》,作用域侧重内容创作与娱乐。sug词条《人类行为心理学》是偏学术的理论知识,虽与行为有弱关联,但品类差异大,覆盖度低,相关性很弱。\n【延伸词维度 -0.15】原始问题聚焦于「制作表情包梗图」这一具体创作行为,并限定了主题「人类双标行为的猫咪表情包」。sug词条「人类行为心理学」是一个宽泛的学术领域,与制作表情包的实践性需求关联度低,且未能体现猫咪和表情包的创作元素,稀释了原始问题的聚焦度。\n【最终得分 0.01】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.045
+    },
+    "sug_人类行为观察员_r2_q21_8": {
+      "type": "sug",
+      "query": "[SUG] 人类行为观察员",
+      "level": 23,
+      "relevance_score": 0.034,
+      "evaluationReason": "【评估对象】词条\"人类行为观察员\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作表情包梗图\n【动机维度 0.00】原始问题的核心动机是「制作」表情包梗图,而sug词条「人类行为观察员」没有包含任何动作意图,因此动机匹配度为0。\n【品类维度 0.08】原始问题涉及“人类行为”和“猫咪表情包梗图”,sug词条仅包含“人类行为”这一部分,且表达过于概括、抽象,没有提及猫咪、表情包等核心限定,覆盖度低。\n【延伸词维度 -0.15】sug词条「人类行为观察员」与原始问题「制作反映人类双标行为的猫咪表情包梗图」的核心目的和对象均不符。它引入了一个与制作表情包无关的新主题,稀释了原始问题的聚焦度,属于作用域稀释型。\n【最终得分 0.03】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.045
+    },
+    "sug_人类行为艺术_r2_q21_9": {
+      "type": "sug",
+      "query": "[SUG] 人类行为艺术",
+      "level": 23,
+      "relevance_score": -0.19000000000000003,
+      "evaluationReason": "【评估对象】词条\"人类行为艺术\" vs 原始问题\"如何制作反映人类双标行为的猫咪表情包梗图\"\n【核心动机】「制作」反映人类双标行为的猫咪表情包梗图\n【动机维度 0.00】原始问题意图是「制作」特定表情包梗图,sug词条「人类行为艺术」没有明确的动作意图。因此,sug词条缺失动机层,无法评估动作匹配度。\n【品类维度 -0.20】原始问题核心是《猫咪表情包梗图》体现《人类双标行为》,sug词条《人类行为艺术》与原始问题主体《猫咪表情包梗图》品类不符,且限定词差异大。\n【延伸词维度 -0.15】sug词条「人类行为艺术」与原始问题「猫咪表情包梗图」的核心对象和目的完全不符,稀释了原始问题的聚焦度,使其偏离核心。\n【最终得分 -0.19】\n【规则说明】情况3:sug词条无动作意图,权重调整为 品类80% + 延伸词20%",
+      "strategy": "推荐词",
+      "iteration": 2,
+      "is_selected": true,
+      "scoreColor": "#ef4444",
+      "parentQScore": 0.045
+    },
+    "step_comb_r2": {
+      "type": "step",
+      "query": "步骤2: 跨2个域组合 (149个组合)",
+      "level": 21,
+      "relevance_score": 0,
+      "strategy": "域内组词",
+      "iteration": 2,
+      "is_selected": true
+    },
+    "comb_如何反映_r2_0": {
+      "type": "domain_combination",
+      "query": "如何反映",
+      "level": 22,
+      "relevance_score": 0.03408,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"如何反映\"\n【评估对象】词条\"如何反映\" vs 作用域词条\"如何反映人类双标行为的\"\n【核心动机】反映\n【动机维度 0.98】词条的核心动作“反映”与原始问题的核心动作“反映”完全一致。\n【品类维度 0.08】词条“如何反映”是一个通用概念,而同一作用域词条“如何反映人类双标行为的”是一个特定概念。词条仅包含同一作用域词条中的动词部分,没有包含任何名词或限定词,因此品类匹配度极低,但由于动词部分相同,给予极低的正分。\n【最终得分 0.71】\n【规则说明】规则A:动机高分保护生效(动机0.98≥0.8),实际得分0.71已≥0.7\n【加权系数计算】\n0.048\n  来源词总得分: 0.05\n  系数: 0.05【计算公式】base_score × 系数 = 0.71 × 0.05\n【最终得分(截断后)】0.03",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+修饰短语]",
+      "source_words": [
+        [
+          "如何"
+        ],
+        [
+          "反映"
+        ]
+      ],
+      "from_segments": [
+        "如何",
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        0,
+        2
+      ],
+      "domains_str": "D0,D2",
+      "source_word_details": [
+        {
+          "domain_index": 0,
+          "segment_type": "疑问引导",
+          "segment_text": "如何",
+          "words": [
+            {
+              "text": "如何",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.024,
+      "scoreColor": "#22c55e"
+    },
+    "comb_如何人类_r2_1": {
+      "type": "domain_combination",
+      "query": "如何人类",
+      "level": 22,
+      "relevance_score": 0.001152,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"如何人类\"\n【评估对象】词条\"如何人类\" vs 作用域词条\"如何反映人类双标行为的\"\n【核心动机】反映\n【动机维度 0.00】词条“如何人类”无明确动作意图,无法评估动作匹配度。\n【品类维度 0.08】词条“如何人类”是通用概念,而同一作用域词条“如何反映人类双标行为的”是特定概念。词条仅包含同一作用域词条中的部分通用词汇“如何”和“人类”,但缺失了核心限定词“双标行为”,导致品类不匹配,因此得分较低。\n【最终得分 0.02】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.02已≤0.5\n【加权系数计算】\n0.048\n  来源词总得分: 0.05\n  系数: 0.05【计算公式】base_score × 系数 = 0.02 × 0.05\n【最终得分(截断后)】0.00",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+修饰短语]",
+      "source_words": [
+        [
+          "如何"
+        ],
+        [
+          "人类"
+        ]
+      ],
+      "from_segments": [
+        "如何",
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        0,
+        2
+      ],
+      "domains_str": "D0,D2",
+      "source_word_details": [
+        {
+          "domain_index": 0,
+          "segment_type": "疑问引导",
+          "segment_text": "如何",
+          "words": [
+            {
+              "text": "如何",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.024,
+      "scoreColor": "#ef4444"
+    },
+    "comb_如何双标_r2_2": {
+      "type": "domain_combination",
+      "query": "如何双标",
+      "level": 22,
+      "relevance_score": 0,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"如何双标\"\n【评估对象】词条\"如何双标\" vs 作用域词条\"如何反映人类双标行为的\"\n【核心动机】反映\n【动机维度 -0.70】原始问题的核心动机是“反映”,即表达、呈现某种现象。而词条的核心动机是“如何双标”,即学习或实践双标行为。两者在动作意图上存在明显的冲突,一个是要呈现,一个是要实施,方向完全相反。\n【品类维度 0.75】核心主体'双标'匹配,但限定词'人类行为'在词条中被简化为动词'如何',导致限定词匹配度降低。\n【最终得分 0.00】\n【规则说明】规则C:动机负向,最终得分=0\n【加权系数计算】\n0.048\n  来源词总得分: 0.05\n  系数: 0.05【计算公式】base_score × 系数 = 0.00 × 0.05\n【最终得分(截断后)】0.00",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+修饰短语]",
+      "source_words": [
+        [
+          "如何"
+        ],
+        [
+          "双标"
+        ]
+      ],
+      "from_segments": [
+        "如何",
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        0,
+        2
+      ],
+      "domains_str": "D0,D2",
+      "source_word_details": [
+        {
+          "domain_index": 0,
+          "segment_type": "疑问引导",
+          "segment_text": "如何",
+          "words": [
+            {
+              "text": "如何",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.024,
+      "scoreColor": "#ef4444"
+    },
+    "comb_如何行为_r2_3": {
+      "type": "domain_combination",
+      "query": "如何行为",
+      "level": 22,
+      "relevance_score": 0.001152,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"如何行为\"\n【评估对象】词条\"如何行为\" vs 作用域词条\"如何反映人类双标行为的\"\n【核心动机】反映\n【动机维度 0.00】原始问题核心动机是“反映”,词条核心动机是“行为”,两者动作意图不匹配。\n【品类维度 0.08】sug词'如何行为'是通用概念,原始问题'如何反映人类双标行为的'是特定概念。通用概念不等于特定概念,因此品类不匹配,评分较低。\n【最终得分 0.02】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.02已≤0.5\n【加权系数计算】\n0.048\n  来源词总得分: 0.05\n  系数: 0.05【计算公式】base_score × 系数 = 0.02 × 0.05\n【最终得分(截断后)】0.00",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+修饰短语]",
+      "source_words": [
+        [
+          "如何"
+        ],
+        [
+          "行为"
+        ]
+      ],
+      "from_segments": [
+        "如何",
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        0,
+        2
+      ],
+      "domains_str": "D0,D2",
+      "source_word_details": [
+        {
+          "domain_index": 0,
+          "segment_type": "疑问引导",
+          "segment_text": "如何",
+          "words": [
+            {
+              "text": "如何",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.024,
+      "scoreColor": "#ef4444"
+    },
+    "comb_如何反映人类_r2_4": {
+      "type": "domain_combination",
+      "query": "如何反映人类",
+      "level": 22,
+      "relevance_score": 0.021195,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"如何反映人类\"\n【评估对象】词条\"如何反映人类\" vs 作用域词条\"如何反映人类双标行为的\"\n【核心动机】反映\n【动机维度 0.00】词条“如何反映人类”的动作意图是“反映”,与原始问题“如何反映人类双标行为的”的动作意图“反映”一致。但词条缺少了原始问题中的核心对象“双标行为”,导致词条的动作意图不完整,无法准确反映原始问题的动机。\n【品类维度 0.75】核心主体'如何反映人类'完全匹配,但限定词'双标行为'缺失,属于核心主体匹配但限定词缺失的情况。\n【最终得分 0.22】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.22已≤0.5\n【加权系数计算】\n0.0942\n  来源词总得分: 0.09\n  系数: 0.09【计算公式】base_score × 系数 = 0.22 × 0.09\n【最终得分(截断后)】0.02",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+修饰短语]",
+      "source_words": [
+        [
+          "如何"
+        ],
+        [
+          "反映",
+          "人类"
+        ]
+      ],
+      "from_segments": [
+        "如何",
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        0,
+        2
+      ],
+      "domains_str": "D0,D2",
+      "source_word_details": [
+        {
+          "domain_index": 0,
+          "segment_type": "疑问引导",
+          "segment_text": "如何",
+          "words": [
+            {
+              "text": "如何",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.024,
+      "scoreColor": "#ef4444"
+    },
+    "comb_如何反映双标_r2_5": {
+      "type": "domain_combination",
+      "query": "如何反映双标",
+      "level": 22,
+      "relevance_score": 0.10038000000000001,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"如何反映双标\"\n【评估对象】词条\"如何反映双标\" vs 作用域词条\"如何反映人类双标行为的\"\n【核心动机】反映\n【动机维度 0.98】原始问题和词条的核心动作都是“反映”,语义完全一致。\n【品类维度 0.90】核心主体'双标'和限定词'反映'均匹配,词条省略了'人类行为',但语义上仍高度相关。\n【最终得分 0.96】\n【规则说明】规则A:动机高分保护生效(动机0.98≥0.8),实际得分0.96已≥0.7\n【加权系数计算】\n0.10500000000000001\n  来源词总得分: 0.11\n  系数: 0.11【计算公式】base_score × 系数 = 0.96 × 0.11\n【最终得分(截断后)】0.10",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+修饰短语]",
+      "source_words": [
+        [
+          "如何"
+        ],
+        [
+          "反映",
+          "双标"
+        ]
+      ],
+      "from_segments": [
+        "如何",
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        0,
+        2
+      ],
+      "domains_str": "D0,D2",
+      "source_word_details": [
+        {
+          "domain_index": 0,
+          "segment_type": "疑问引导",
+          "segment_text": "如何",
+          "words": [
+            {
+              "text": "如何",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.024,
+      "scoreColor": "#22c55e"
+    },
+    "comb_如何反映行为_r2_6": {
+      "type": "domain_combination",
+      "query": "如何反映行为",
+      "level": 22,
+      "relevance_score": 0.028423199999999996,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"如何反映行为\"\n【评估对象】词条\"如何反映行为\" vs 作用域词条\"如何反映人类双标行为的\"\n【核心动机】反映\n【动机维度 0.98】原始问题和词条的核心动作都是“反映”,动作意图完全一致。\n【品类维度 0.75】核心主体'行为'匹配,但限定词'人类双标'缺失,属于核心主体匹配但限定词不完全匹配的情况。\n【最终得分 0.91】\n【规则说明】规则A:动机高分保护生效(动机0.98≥0.8),实际得分0.91已≥0.7\n【加权系数计算】\n0.0312\n  来源词总得分: 0.03\n  系数: 0.03【计算公式】base_score × 系数 = 0.91 × 0.03\n【最终得分(截断后)】0.03",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+修饰短语]",
+      "source_words": [
+        [
+          "如何"
+        ],
+        [
+          "反映",
+          "行为"
+        ]
+      ],
+      "from_segments": [
+        "如何",
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        0,
+        2
+      ],
+      "domains_str": "D0,D2",
+      "source_word_details": [
+        {
+          "domain_index": 0,
+          "segment_type": "疑问引导",
+          "segment_text": "如何",
+          "words": [
+            {
+              "text": "如何",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.024,
+      "scoreColor": "#22c55e"
+    },
+    "comb_如何人类双标_r2_7": {
+      "type": "domain_combination",
+      "query": "如何人类双标",
+      "level": 22,
+      "relevance_score": 0.028350000000000004,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"如何人类双标\"\n【评估对象】词条\"如何人类双标\" vs 作用域词条\"如何反映人类双标行为的\"\n【核心动机】反映\n【动机维度 0.00】词条无明确动作意图,无法评估动作匹配度\n【品类维度 0.90】核心主体'人类双标'完全匹配,限定词'如何'也匹配,但词条中缺少了'行为的',导致匹配度略有下降。\n【最终得分 0.27】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.27已≤0.5\n【加权系数计算】\n0.10500000000000001\n  来源词总得分: 0.11\n  系数: 0.11【计算公式】base_score × 系数 = 0.27 × 0.11\n【最终得分(截断后)】0.03",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+修饰短语]",
+      "source_words": [
+        [
+          "如何"
+        ],
+        [
+          "人类",
+          "双标"
+        ]
+      ],
+      "from_segments": [
+        "如何",
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        0,
+        2
+      ],
+      "domains_str": "D0,D2",
+      "source_word_details": [
+        {
+          "domain_index": 0,
+          "segment_type": "疑问引导",
+          "segment_text": "如何",
+          "words": [
+            {
+              "text": "如何",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.024,
+      "scoreColor": "#22c55e"
+    },
+    "comb_如何人类行为_r2_8": {
+      "type": "domain_combination",
+      "query": "如何人类行为",
+      "level": 22,
+      "relevance_score": 0.014490000000000001,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"如何人类行为\"\n【评估对象】词条\"如何人类行为\" vs 作用域词条\"如何反映人类双标行为的\"\n【核心动机】反映\n【动机维度 0.00】词条“如何人类行为”没有明确的动作意图,无法与原始问题的“反映”动作进行匹配。\n【品类维度 0.70】核心主体词'人类行为'匹配,但限定词'双标'缺失,属于核心主体匹配但限定词不完全匹配的情况。\n【最终得分 0.21】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.21已≤0.5\n【加权系数计算】\n0.069\n  来源词总得分: 0.07\n  系数: 0.07【计算公式】base_score × 系数 = 0.21 × 0.07\n【最终得分(截断后)】0.01",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+修饰短语]",
+      "source_words": [
+        [
+          "如何"
+        ],
+        [
+          "人类",
+          "行为"
+        ]
+      ],
+      "from_segments": [
+        "如何",
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        0,
+        2
+      ],
+      "domains_str": "D0,D2",
+      "source_word_details": [
+        {
+          "domain_index": 0,
+          "segment_type": "疑问引导",
+          "segment_text": "如何",
+          "words": [
+            {
+              "text": "如何",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.024,
+      "scoreColor": "#ef4444"
+    },
+    "comb_如何双标行为_r2_9": {
+      "type": "domain_combination",
+      "query": "如何双标行为",
+      "level": 22,
+      "relevance_score": 0.0226125,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"如何双标行为\"\n【评估对象】词条\"如何双标行为\" vs 作用域词条\"如何反映人类双标行为的\"\n【核心动机】反映\n【动机维度 0.00】原始问题核心动机是'反映',词条没有明确的动作意图,无法评估动机匹配度。\n【品类维度 0.75】核心主体'双标行为'匹配,但限定词'如何反映人类'与'如何'存在差异,后者更直接指向行为本身,而非其反映方式。\n【最终得分 0.22】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.22已≤0.5\n【加权系数计算】\n0.1005\n  来源词总得分: 0.10\n  系数: 0.10【计算公式】base_score × 系数 = 0.22 × 0.10\n【最终得分(截断后)】0.02",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+修饰短语]",
+      "source_words": [
+        [
+          "如何"
+        ],
+        [
+          "双标",
+          "行为"
+        ]
+      ],
+      "from_segments": [
+        "如何",
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        0,
+        2
+      ],
+      "domains_str": "D0,D2",
+      "source_word_details": [
+        {
+          "domain_index": 0,
+          "segment_type": "疑问引导",
+          "segment_text": "如何",
+          "words": [
+            {
+              "text": "如何",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.024,
+      "scoreColor": "#ef4444"
+    },
+    "comb_如何反映人类双标_r2_10": {
+      "type": "domain_combination",
+      "query": "如何反映人类双标",
+      "level": 22,
+      "relevance_score": 0.109956,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"如何反映人类双标\"\n【评估对象】词条\"如何反映人类双标\" vs 作用域词条\"如何反映人类双标行为的\"\n【核心动机】反映\n【动机维度 0.98】原始问题和词条的核心动作都是“反映”,语义完全一致。\n【品类维度 0.98】核心主体“人类双标行为”与“人类双标”高度匹配,仅在限定词上存在微小差异(“行为的”与空),但语义上完全一致,因此匹配度极高。\n【最终得分 0.98】\n【规则说明】规则A:动机高分保护生效(动机0.98≥0.8),实际得分0.98已≥0.7\n【加权系数计算】\n0.1122\n  来源词总得分: 0.11\n  系数: 0.11【计算公式】base_score × 系数 = 0.98 × 0.11\n【最终得分(截断后)】0.11",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+修饰短语]",
+      "source_words": [
+        [
+          "如何"
+        ],
+        [
+          "反映",
+          "人类",
+          "双标"
+        ]
+      ],
+      "from_segments": [
+        "如何",
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        0,
+        2
+      ],
+      "domains_str": "D0,D2",
+      "source_word_details": [
+        {
+          "domain_index": 0,
+          "segment_type": "疑问引导",
+          "segment_text": "如何",
+          "words": [
+            {
+              "text": "如何",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.024,
+      "scoreColor": "#22c55e"
+    },
+    "comb_如何反映人类行为_r2_11": {
+      "type": "domain_combination",
+      "query": "如何反映人类行为",
+      "level": 22,
+      "relevance_score": 0.080541,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"如何反映人类行为\"\n【评估对象】词条\"如何反映人类行为\" vs 作用域词条\"如何反映人类双标行为的\"\n【核心动机】反映\n【动机维度 0.90】原始问题和词条的核心动机都是“反映”,动机完全一致。\n【品类维度 0.75】核心主体“人类行为”匹配,限定词“双标”缺失,属于合理泛化。\n【最终得分 0.85】\n【规则说明】规则A:动机高分保护生效(动机0.90≥0.8),实际得分0.85已≥0.7\n【加权系数计算】\n0.0942\n  来源词总得分: 0.09\n  系数: 0.09【计算公式】base_score × 系数 = 0.85 × 0.09\n【最终得分(截断后)】0.08",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+修饰短语]",
+      "source_words": [
+        [
+          "如何"
+        ],
+        [
+          "反映",
+          "人类",
+          "行为"
+        ]
+      ],
+      "from_segments": [
+        "如何",
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        0,
+        2
+      ],
+      "domains_str": "D0,D2",
+      "source_word_details": [
+        {
+          "domain_index": 0,
+          "segment_type": "疑问引导",
+          "segment_text": "如何",
+          "words": [
+            {
+              "text": "如何",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.024,
+      "scoreColor": "#22c55e"
+    },
+    "comb_如何反映双标行为_r2_12": {
+      "type": "domain_combination",
+      "query": "如何反映双标行为",
+      "level": 22,
+      "relevance_score": 0.096078,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"如何反映双标行为\"\n【评估对象】词条\"如何反映双标行为\" vs 作用域词条\"如何反映人类双标行为的\"\n【核心动机】反映\n【动机维度 0.98】原始问题和词条的核心动作都是“反映”,语义完全一致。\n【品类维度 0.90】核心主体'双标行为'完全匹配,限定词'人类'在词条中被省略,但语义上仍指代人类双标行为,属于合理泛化,因此匹配度高。\n【最终得分 0.96】\n【规则说明】规则A:动机高分保护生效(动机0.98≥0.8),实际得分0.96已≥0.7\n【加权系数计算】\n0.1005\n  来源词总得分: 0.10\n  系数: 0.10【计算公式】base_score × 系数 = 0.96 × 0.10\n【最终得分(截断后)】0.10",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+修饰短语]",
+      "source_words": [
+        [
+          "如何"
+        ],
+        [
+          "反映",
+          "双标",
+          "行为"
+        ]
+      ],
+      "from_segments": [
+        "如何",
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        0,
+        2
+      ],
+      "domains_str": "D0,D2",
+      "source_word_details": [
+        {
+          "domain_index": 0,
+          "segment_type": "疑问引导",
+          "segment_text": "如何",
+          "words": [
+            {
+              "text": "如何",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.024,
+      "scoreColor": "#22c55e"
+    },
+    "comb_如何人类双标行为_r2_13": {
+      "type": "domain_combination",
+      "query": "如何人类双标行为",
+      "level": 22,
+      "relevance_score": 0.029925,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"如何人类双标行为\"\n【评估对象】词条\"如何人类双标行为\" vs 作用域词条\"如何反映人类双标行为的\"\n【核心动机】反映\n【动机维度 0.00】词条无明确动作意图,无法评估动作匹配度\n【品类维度 0.95】核心主体“人类双标行为”完全匹配,限定词“如何反映”与“如何”高度匹配,仅缺少“反映”一词,但语义上仍保持一致性。\n【最终得分 0.28】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.28已≤0.5\n【加权系数计算】\n0.10500000000000001\n  来源词总得分: 0.11\n  系数: 0.11【计算公式】base_score × 系数 = 0.28 × 0.11\n【最终得分(截断后)】0.03",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+修饰短语]",
+      "source_words": [
+        [
+          "如何"
+        ],
+        [
+          "人类",
+          "双标",
+          "行为"
+        ]
+      ],
+      "from_segments": [
+        "如何",
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        0,
+        2
+      ],
+      "domains_str": "D0,D2",
+      "source_word_details": [
+        {
+          "domain_index": 0,
+          "segment_type": "疑问引导",
+          "segment_text": "如何",
+          "words": [
+            {
+              "text": "如何",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.024,
+      "scoreColor": "#22c55e"
+    },
+    "comb_如何反映人类双标行为_r2_14": {
+      "type": "domain_combination",
+      "query": "如何反映人类双标行为",
+      "level": 22,
+      "relevance_score": 0.1122,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"如何反映人类双标行为\"\n【评估对象】词条\"如何反映人类双标行为\" vs 作用域词条\"如何反映人类双标行为的\"\n【核心动机】反映\n【动机维度 1.00】词条与原始问题的核心动机完全一致,都是“反映”。\n【品类维度 1.00】核心主体和所有关键限定词完全匹配,词条与同一作用域词条完全一致。\n【最终得分 1.00】\n【规则说明】规则A:动机高分保护生效(动机1.00≥0.8),实际得分1.00已≥0.7\n【加权系数计算】\n0.1122\n  来源词总得分: 0.11\n  系数: 0.11【计算公式】base_score × 系数 = 1.00 × 0.11\n【最终得分(截断后)】0.11",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+修饰短语]",
+      "source_words": [
+        [
+          "如何"
+        ],
+        [
+          "反映",
+          "人类",
+          "双标",
+          "行为"
+        ]
+      ],
+      "from_segments": [
+        "如何",
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        0,
+        2
+      ],
+      "domains_str": "D0,D2",
+      "source_word_details": [
+        {
+          "domain_index": 0,
+          "segment_type": "疑问引导",
+          "segment_text": "如何",
+          "words": [
+            {
+              "text": "如何",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.024,
+      "scoreColor": "#22c55e"
+    },
+    "comb_如何猫咪_r2_15": {
+      "type": "domain_combination",
+      "query": "如何猫咪",
+      "level": 22,
+      "relevance_score": 0.011969999999999998,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"如何猫咪\"\n【评估对象】词条\"如何猫咪\" vs 作用域词条\"如何猫咪表情包梗图\"\n【核心动机】制作/生成猫咪表情包梗图\n【动机维度 0.00】词条“如何猫咪”无明确动作意图,无法评估动作匹配度。\n【品类维度 0.35】核心主体'猫咪'匹配,但限定词'表情包梗图'缺失,导致品类泛化。\n【最终得分 0.10】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.10已≤0.5\n【加权系数计算】\n0.11399999999999999\n  来源词总得分: 0.11\n  系数: 0.11【计算公式】base_score × 系数 = 0.10 × 0.11\n【最终得分(截断后)】0.01",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+中心名词]",
+      "source_words": [
+        [
+          "如何"
+        ],
+        [
+          "猫咪"
+        ]
+      ],
+      "from_segments": [
+        "如何",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        0,
+        3
+      ],
+      "domains_str": "D0,D3",
+      "source_word_details": [
+        {
+          "domain_index": 0,
+          "segment_type": "疑问引导",
+          "segment_text": "如何",
+          "words": [
+            {
+              "text": "如何",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.09
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.09,
+      "scoreColor": "#ef4444"
+    },
+    "comb_如何表情包_r2_16": {
+      "type": "domain_combination",
+      "query": "如何表情包",
+      "level": 22,
+      "relevance_score": 0.14876999999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"如何表情包\"\n【评估对象】词条\"如何表情包\" vs 作用域词条\"如何猫咪表情包梗图\"\n【核心动机】制作/生成猫咪表情包梗图\n【动机维度 0.90】原始问题和词条的核心动机都是'制作/生成表情包',词条是原始问题动作的泛化,动机高度一致。\n【品类维度 0.75】核心主体“表情包”匹配,但限定词“猫咪”缺失,属于合理泛化。\n【最终得分 0.85】\n【规则说明】规则A:动机高分保护生效(动机0.90≥0.8),实际得分0.85已≥0.7\n【加权系数计算】\n0.174\n  来源词总得分: 0.17\n  系数: 0.17【计算公式】base_score × 系数 = 0.85 × 0.17\n【最终得分(截断后)】0.15",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+中心名词]",
+      "source_words": [
+        [
+          "如何"
+        ],
+        [
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "如何",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        0,
+        3
+      ],
+      "domains_str": "D0,D3",
+      "source_word_details": [
+        {
+          "domain_index": 0,
+          "segment_type": "疑问引导",
+          "segment_text": "如何",
+          "words": [
+            {
+              "text": "如何",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.15
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_如何梗图_r2_17": {
+      "type": "domain_combination",
+      "query": "如何梗图",
+      "level": 22,
+      "relevance_score": 0.0336,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"如何梗图\"\n【评估对象】词条\"如何梗图\" vs 作用域词条\"如何猫咪表情包梗图\"\n【核心动机】制作/生成猫咪表情包梗图\n【动机维度 0.90】原始问题和词条的核心动机都是'制作/生成梗图',词条是原始问题动机的泛化,动机高度一致。\n【品类维度 0.08】原始问题是'猫咪表情包梗图',限定了主体是'猫咪表情包'。词条是'梗图',是一个通用概念,没有限定主体。通用概念不等于特定概念,因此品类匹配度低。\n【最终得分 0.70】\n【规则说明】规则A:动机高分保护(动机0.90≥0.8),最终得分下限=0.7\n【加权系数计算】\n0.048\n  来源词总得分: 0.05\n  系数: 0.05【计算公式】base_score × 系数 = 0.70 × 0.05\n【最终得分(截断后)】0.03",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+中心名词]",
+      "source_words": [
+        [
+          "如何"
+        ],
+        [
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "如何",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        0,
+        3
+      ],
+      "domains_str": "D0,D3",
+      "source_word_details": [
+        {
+          "domain_index": 0,
+          "segment_type": "疑问引导",
+          "segment_text": "如何",
+          "words": [
+            {
+              "text": "如何",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.024,
+      "scoreColor": "#22c55e"
+    },
+    "comb_如何猫咪表情包_r2_18": {
+      "type": "domain_combination",
+      "query": "如何猫咪表情包",
+      "level": 22,
+      "relevance_score": 0.22427759999999997,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"如何猫咪表情包\"\n【评估对象】词条\"如何猫咪表情包\" vs 作用域词条\"如何猫咪表情包梗图\"\n【核心动机】制作/获取猫咪表情包梗图\n【动机维度 0.98】原始问题和词条的核心动机都是关于“如何制作/获取猫咪表情包”,动机完全一致。\n【品类维度 0.90】核心主体“猫咪表情包”完全匹配,限定词“梗图”在词条中缺失,但“表情包”通常包含“梗图”的含义,属于合理泛化,因此匹配度较高。\n【最终得分 0.96】\n【规则说明】规则A:动机高分保护生效(动机0.98≥0.8),实际得分0.96已≥0.7\n【加权系数计算】\n0.23459999999999998\n  来源词总得分: 0.23\n  系数: 0.23【计算公式】base_score × 系数 = 0.96 × 0.23\n【最终得分(截断后)】0.22",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+中心名词]",
+      "source_words": [
+        [
+          "如何"
+        ],
+        [
+          "猫咪",
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "如何",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        0,
+        3
+      ],
+      "domains_str": "D0,D3",
+      "source_word_details": [
+        {
+          "domain_index": 0,
+          "segment_type": "疑问引导",
+          "segment_text": "如何",
+          "words": [
+            {
+              "text": "如何",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.09,
+        0.15
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.15,
+      "scoreColor": "#22c55e"
+    },
+    "comb_如何猫咪梗图_r2_19": {
+      "type": "domain_combination",
+      "query": "如何猫咪梗图",
+      "level": 22,
+      "relevance_score": 0.22427759999999997,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"如何猫咪梗图\"\n【评估对象】词条\"如何猫咪梗图\" vs 作用域词条\"如何猫咪表情包梗图\"\n【核心动机】制作/生成猫咪表情包梗图\n【动机维度 0.98】原始问题和词条的核心动机都是'制作/生成',词条是原始问题的简化表达,但动机完全一致。\n【品类维度 0.90】核心主体“猫咪梗图”完全匹配,限定词“表情包”在词条中缺失,但“梗图”本身就包含“表情包”的含义,属于合理泛化,因此匹配度高。\n【最终得分 0.96】\n【规则说明】规则A:动机高分保护生效(动机0.98≥0.8),实际得分0.96已≥0.7\n【加权系数计算】\n0.23459999999999998\n  来源词总得分: 0.23\n  系数: 0.23【计算公式】base_score × 系数 = 0.96 × 0.23\n【最终得分(截断后)】0.22",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+中心名词]",
+      "source_words": [
+        [
+          "如何"
+        ],
+        [
+          "猫咪",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "如何",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        0,
+        3
+      ],
+      "domains_str": "D0,D3",
+      "source_word_details": [
+        {
+          "domain_index": 0,
+          "segment_type": "疑问引导",
+          "segment_text": "如何",
+          "words": [
+            {
+              "text": "如何",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.09,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.09,
+      "scoreColor": "#22c55e"
+    },
+    "comb_如何表情包梗图_r2_20": {
+      "type": "domain_combination",
+      "query": "如何表情包梗图",
+      "level": 22,
+      "relevance_score": 0.1705725,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"如何表情包梗图\"\n【评估对象】词条\"如何表情包梗图\" vs 作用域词条\"如何猫咪表情包梗图\"\n【核心动机】制作/生成\n【动机维度 0.90】原始问题和词条的核心动机都是'制作/生成',动机完全一致。\n【品类维度 0.75】核心主体“表情包梗图”匹配,但限定词“猫咪”缺失。\n【最终得分 0.85】\n【规则说明】规则A:动机高分保护生效(动机0.90≥0.8),实际得分0.85已≥0.7\n【加权系数计算】\n0.19949999999999998\n  来源词总得分: 0.20\n  系数: 0.20【计算公式】base_score × 系数 = 0.85 × 0.20\n【最终得分(截断后)】0.17",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+中心名词]",
+      "source_words": [
+        [
+          "如何"
+        ],
+        [
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "如何",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        0,
+        3
+      ],
+      "domains_str": "D0,D3",
+      "source_word_details": [
+        {
+          "domain_index": 0,
+          "segment_type": "疑问引导",
+          "segment_text": "如何",
+          "words": [
+            {
+              "text": "如何",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.15,
+      "scoreColor": "#22c55e"
+    },
+    "comb_如何猫咪表情包梗图_r2_21": {
+      "type": "domain_combination",
+      "query": "如何猫咪表情包梗图",
+      "level": 22,
+      "relevance_score": 0.258,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"如何猫咪表情包梗图\"\n【评估对象】词条\"如何猫咪表情包梗图\" vs 作用域词条\"如何猫咪表情包梗图\"\n【核心动机】制作/生成猫咪表情包梗图\n【动机维度 1.00】词条与原始问题的核心动机完全一致,都是关于'如何制作/生成猫咪表情包梗图'。\n【品类维度 1.00】核心主体和所有关键限定词完全匹配,词条与同一作用域词条完全一致。\n【最终得分 1.00】\n【规则说明】规则A:动机高分保护生效(动机1.00≥0.8),实际得分1.00已≥0.7\n【加权系数计算】\n0.258\n  来源词总得分: 0.26\n  系数: 0.26【计算公式】base_score × 系数 = 1.00 × 0.26\n【最终得分(截断后)】0.26",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+中心名词]",
+      "source_words": [
+        [
+          "如何"
+        ],
+        [
+          "猫咪",
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "如何",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        0,
+        3
+      ],
+      "domains_str": "D0,D3",
+      "source_word_details": [
+        {
+          "domain_index": 0,
+          "segment_type": "疑问引导",
+          "segment_text": "如何",
+          "words": [
+            {
+              "text": "如何",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.09,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.15,
+      "scoreColor": "#22c55e"
+    },
+    "comb_制作反映_r2_22": {
+      "type": "domain_combination",
+      "query": "制作反映",
+      "level": 22,
+      "relevance_score": 0.5211399999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"制作反映\"\n【评估对象】词条\"制作反映\" vs 作用域词条\"制作反映人类双标行为的\"\n【核心动机】制作\n【动机维度 0.98】原始问题的核心动机是“制作”,词条的核心动机也是“制作”,两者动作意图完全一致。\n【品类维度 0.08】sug词是通用概念,原始问题是特定概念。sug词只有'制作反映',无'人类双标行为'字眼,品类不匹配。\n【最终得分 0.71】\n【规则说明】规则A:动机高分保护生效(动机0.98≥0.8),实际得分0.71已≥0.7\n【加权系数计算】\n0.734\n  来源词总得分: 0.73\n  系数: 0.73【计算公式】base_score × 系数 = 0.71 × 0.73\n【最终得分(截断后)】0.52",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+修饰短语]",
+      "source_words": [
+        [
+          "制作"
+        ],
+        [
+          "反映"
+        ]
+      ],
+      "from_segments": [
+        "制作",
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        1,
+        2
+      ],
+      "domains_str": "D1,D2",
+      "source_word_details": [
+        {
+          "domain_index": 1,
+          "segment_type": "核心动作",
+          "segment_text": "制作",
+          "words": [
+            {
+              "text": "制作",
+              "score": 0.71
+            }
+          ]
+        },
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.71,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.71,
+      "scoreColor": "#ef4444"
+    },
+    "comb_制作人类_r2_23": {
+      "type": "domain_combination",
+      "query": "制作人类",
+      "level": 22,
+      "relevance_score": 0.5475639999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"制作人类\"\n【评估对象】词条\"制作人类\" vs 作用域词条\"制作反映人类双标行为的\"\n【核心动机】制作\n【动机维度 0.98】原始问题和词条的核心动作都是“制作”,动作意图完全一致。\n【品类维度 0.20】词条“制作人类”与同一作用域词条“制作反映人类双标行为的”在核心主体“制作”和“人类”上存在部分匹配,但词条缺少了限定词“双标行为”,导致品类匹配度较低。\n【最终得分 0.75】\n【规则说明】规则A:动机高分保护生效(动机0.98≥0.8),实际得分0.75已≥0.7\n【加权系数计算】\n0.734\n  来源词总得分: 0.73\n  系数: 0.73【计算公式】base_score × 系数 = 0.75 × 0.73\n【最终得分(截断后)】0.55",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+修饰短语]",
+      "source_words": [
+        [
+          "制作"
+        ],
+        [
+          "人类"
+        ]
+      ],
+      "from_segments": [
+        "制作",
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        1,
+        2
+      ],
+      "domains_str": "D1,D2",
+      "source_word_details": [
+        {
+          "domain_index": 1,
+          "segment_type": "核心动作",
+          "segment_text": "制作",
+          "words": [
+            {
+              "text": "制作",
+              "score": 0.71
+            }
+          ]
+        },
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.71,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.71,
+      "scoreColor": "#ef4444"
+    },
+    "comb_制作双标_r2_24": {
+      "type": "domain_combination",
+      "query": "制作双标",
+      "level": 22,
+      "relevance_score": 0.6576639999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"制作双标\"\n【评估对象】词条\"制作双标\" vs 作用域词条\"制作反映人类双标行为的\"\n【核心动机】制作\n【动机维度 0.98】原始问题的核心动机是“制作”,词条的核心动机也是“制作”,两者动作意图完全一致。\n【品类维度 0.70】核心主体'双标'匹配,但原始问题中的限定词'人类行为'在词条中缺失,词条'制作双标'可以理解为制作关于双标的内容,但未明确限定是人类行为。\n【最终得分 0.90】\n【规则说明】规则A:动机高分保护生效(动机0.98≥0.8),实际得分0.90已≥0.7\n【加权系数计算】\n0.734\n  来源词总得分: 0.73\n  系数: 0.73【计算公式】base_score × 系数 = 0.90 × 0.73\n【最终得分(截断后)】0.66",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+修饰短语]",
+      "source_words": [
+        [
+          "制作"
+        ],
+        [
+          "双标"
+        ]
+      ],
+      "from_segments": [
+        "制作",
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        1,
+        2
+      ],
+      "domains_str": "D1,D2",
+      "source_word_details": [
+        {
+          "domain_index": 1,
+          "segment_type": "核心动作",
+          "segment_text": "制作",
+          "words": [
+            {
+              "text": "制作",
+              "score": 0.71
+            }
+          ]
+        },
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.71,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.71,
+      "scoreColor": "#ef4444"
+    },
+    "comb_制作行为_r2_25": {
+      "type": "domain_combination",
+      "query": "制作行为",
+      "level": 22,
+      "relevance_score": 0.5211399999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"制作行为\"\n【评估对象】词条\"制作行为\" vs 作用域词条\"制作反映人类双标行为的\"\n【核心动机】制作\n【动机维度 0.98】原始问题的核心动机是'制作',词条的核心动机也是'制作',两者动作意图完全一致。\n【品类维度 0.08】原始问题是特定概念“反映人类双标行为的”,而词条“制作行为”是通用概念,品类不匹配,因此得分较低。\n【最终得分 0.71】\n【规则说明】规则A:动机高分保护生效(动机0.98≥0.8),实际得分0.71已≥0.7\n【加权系数计算】\n0.734\n  来源词总得分: 0.73\n  系数: 0.73【计算公式】base_score × 系数 = 0.71 × 0.73\n【最终得分(截断后)】0.52",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+修饰短语]",
+      "source_words": [
+        [
+          "制作"
+        ],
+        [
+          "行为"
+        ]
+      ],
+      "from_segments": [
+        "制作",
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        1,
+        2
+      ],
+      "domains_str": "D1,D2",
+      "source_word_details": [
+        {
+          "domain_index": 1,
+          "segment_type": "核心动作",
+          "segment_text": "制作",
+          "words": [
+            {
+              "text": "制作",
+              "score": 0.71
+            }
+          ]
+        },
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.71,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.71,
+      "scoreColor": "#ef4444"
+    },
+    "comb_制作反映人类_r2_26": {
+      "type": "domain_combination",
+      "query": "制作反映人类",
+      "level": 22,
+      "relevance_score": 0.667071,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"制作反映人类\"\n【评估对象】词条\"制作反映人类\" vs 作用域词条\"制作反映人类双标行为的\"\n【核心动机】制作\n【动机维度 0.90】原始问题和词条的核心动机都是“制作”,词条是原始问题动机的泛化表达,动机一致。\n【品类维度 0.75】核心主体“制作反映人类”完全匹配,但缺少了限定词“双标行为的”,属于核心主体匹配,存在限定词匹配的情况。\n【最终得分 0.85】\n【规则说明】规则A:动机高分保护生效(动机0.90≥0.8),实际得分0.85已≥0.7\n【加权系数计算】\n0.7802\n  来源词总得分: 0.78\n  系数: 0.78【计算公式】base_score × 系数 = 0.85 × 0.78\n【最终得分(截断后)】0.67",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+修饰短语]",
+      "source_words": [
+        [
+          "制作"
+        ],
+        [
+          "反映",
+          "人类"
+        ]
+      ],
+      "from_segments": [
+        "制作",
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        1,
+        2
+      ],
+      "domains_str": "D1,D2",
+      "source_word_details": [
+        {
+          "domain_index": 1,
+          "segment_type": "核心动作",
+          "segment_text": "制作",
+          "words": [
+            {
+              "text": "制作",
+              "score": 0.71
+            }
+          ]
+        },
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.71,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.71,
+      "scoreColor": "#ef4444"
+    },
+    "comb_制作反映双标_r2_27": {
+      "type": "domain_combination",
+      "query": "制作反映双标",
+      "level": 22,
+      "relevance_score": 0.7443309999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"制作反映双标\"\n【评估对象】词条\"制作反映双标\" vs 作用域词条\"制作反映人类双标行为的\"\n【核心动机】制作\n【动机维度 0.98】原始问题的核心动机是'制作',词条的核心动机也是'制作',两者动作意图完全一致。\n【品类维度 0.85】核心主体“双标”和限定词“反映”均匹配,但缺少了“人类行为”这一限定词,属于核心主体匹配,存在限定词匹配的情况。\n【最终得分 0.94】\n【规则说明】规则A:动机高分保护生效(动机0.98≥0.8),实际得分0.94已≥0.7\n【加权系数计算】\n0.7909999999999999\n  来源词总得分: 0.79\n  系数: 0.79【计算公式】base_score × 系数 = 0.94 × 0.79\n【最终得分(截断后)】0.74",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+修饰短语]",
+      "source_words": [
+        [
+          "制作"
+        ],
+        [
+          "反映",
+          "双标"
+        ]
+      ],
+      "from_segments": [
+        "制作",
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        1,
+        2
+      ],
+      "domains_str": "D1,D2",
+      "source_word_details": [
+        {
+          "domain_index": 1,
+          "segment_type": "核心动作",
+          "segment_text": "制作",
+          "words": [
+            {
+              "text": "制作",
+              "score": 0.71
+            }
+          ]
+        },
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.71,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.71,
+      "scoreColor": "#22c55e"
+    },
+    "comb_制作反映行为_r2_28": {
+      "type": "domain_combination",
+      "query": "制作反映行为",
+      "level": 22,
+      "relevance_score": 0.5092119999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"制作反映行为\"\n【评估对象】词条\"制作反映行为\" vs 作用域词条\"制作反映人类双标行为的\"\n【核心动机】制作\n【动机维度 0.98】原始问题和词条的核心动作都是“制作”,动作意图完全一致。\n【品类维度 0.08】sug词是通用概念“制作反映行为”,原始问题是特定概念“制作反映人类双标行为的”。通用概念不等于特定概念,因此品类不匹配,评分较低。\n【最终得分 0.71】\n【规则说明】规则A:动机高分保护生效(动机0.98≥0.8),实际得分0.71已≥0.7\n【加权系数计算】\n0.7172\n  来源词总得分: 0.72\n  系数: 0.72【计算公式】base_score × 系数 = 0.71 × 0.72\n【最终得分(截断后)】0.51",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+修饰短语]",
+      "source_words": [
+        [
+          "制作"
+        ],
+        [
+          "反映",
+          "行为"
+        ]
+      ],
+      "from_segments": [
+        "制作",
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        1,
+        2
+      ],
+      "domains_str": "D1,D2",
+      "source_word_details": [
+        {
+          "domain_index": 1,
+          "segment_type": "核心动作",
+          "segment_text": "制作",
+          "words": [
+            {
+              "text": "制作",
+              "score": 0.71
+            }
+          ]
+        },
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.71,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.71,
+      "scoreColor": "#ef4444"
+    },
+    "comb_制作人类双标_r2_29": {
+      "type": "domain_combination",
+      "query": "制作人类双标",
+      "level": 22,
+      "relevance_score": 0.7561959999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"制作人类双标\"\n【评估对象】词条\"制作人类双标\" vs 作用域词条\"制作反映人类双标行为的\"\n【核心动机】制作\n【动机维度 0.98】原始问题和词条的核心动作都是“制作”,动机意图完全一致。\n【品类维度 0.90】核心主体“人类双标”完全匹配,限定词“制作”也匹配,但缺少“行为的”这个限定词,因此略有扣分。\n【最终得分 0.96】\n【规则说明】规则A:动机高分保护生效(动机0.98≥0.8),实际得分0.96已≥0.7\n【加权系数计算】\n0.7909999999999999\n  来源词总得分: 0.79\n  系数: 0.79【计算公式】base_score × 系数 = 0.96 × 0.79\n【最终得分(截断后)】0.76",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+修饰短语]",
+      "source_words": [
+        [
+          "制作"
+        ],
+        [
+          "人类",
+          "双标"
+        ]
+      ],
+      "from_segments": [
+        "制作",
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        1,
+        2
+      ],
+      "domains_str": "D1,D2",
+      "source_word_details": [
+        {
+          "domain_index": 1,
+          "segment_type": "核心动作",
+          "segment_text": "制作",
+          "words": [
+            {
+              "text": "制作",
+              "score": 0.71
+            }
+          ]
+        },
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.71,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.71,
+      "scoreColor": "#22c55e"
+    },
+    "comb_制作人类行为_r2_30": {
+      "type": "domain_combination",
+      "query": "制作人类行为",
+      "level": 22,
+      "relevance_score": 0.67648,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"制作人类行为\"\n【评估对象】词条\"制作人类行为\" vs 作用域词条\"制作反映人类双标行为的\"\n【核心动机】制作\n【动机维度 0.98】原始问题和词条的核心动作都是“制作”,动作意图完全一致。\n【品类维度 0.70】核心主体“制作人类行为”与“制作反映人类双标行为的”匹配,但限定词“双标”缺失,属于核心主体匹配但限定词不完全匹配的情况。\n【最终得分 0.90】\n【规则说明】规则A:动机高分保护生效(动机0.98≥0.8),实际得分0.90已≥0.7\n【加权系数计算】\n0.755\n  来源词总得分: 0.76\n  系数: 0.76【计算公式】base_score × 系数 = 0.90 × 0.76\n【最终得分(截断后)】0.68",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+修饰短语]",
+      "source_words": [
+        [
+          "制作"
+        ],
+        [
+          "人类",
+          "行为"
+        ]
+      ],
+      "from_segments": [
+        "制作",
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        1,
+        2
+      ],
+      "domains_str": "D1,D2",
+      "source_word_details": [
+        {
+          "domain_index": 1,
+          "segment_type": "核心动作",
+          "segment_text": "制作",
+          "words": [
+            {
+              "text": "制作",
+              "score": 0.71
+            }
+          ]
+        },
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.71,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.71,
+      "scoreColor": "#ef4444"
+    },
+    "comb_制作双标行为_r2_31": {
+      "type": "domain_combination",
+      "query": "制作双标行为",
+      "level": 22,
+      "relevance_score": 0.7235799999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"制作双标行为\"\n【评估对象】词条\"制作双标行为\" vs 作用域词条\"制作反映人类双标行为的\"\n【核心动机】制作\n【动机维度 0.98】原始问题和词条的核心动作意图都是“制作”,语义完全一致。\n【品类维度 0.78】核心主体词“制作”和“双标行为”均匹配,但缺少了限定词“人类”和“反映”,因此给予较高但非满分评分。\n【最终得分 0.92】\n【规则说明】规则A:动机高分保护生效(动机0.98≥0.8),实际得分0.92已≥0.7\n【加权系数计算】\n0.7865\n  来源词总得分: 0.79\n  系数: 0.79【计算公式】base_score × 系数 = 0.92 × 0.79\n【最终得分(截断后)】0.72",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+修饰短语]",
+      "source_words": [
+        [
+          "制作"
+        ],
+        [
+          "双标",
+          "行为"
+        ]
+      ],
+      "from_segments": [
+        "制作",
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        1,
+        2
+      ],
+      "domains_str": "D1,D2",
+      "source_word_details": [
+        {
+          "domain_index": 1,
+          "segment_type": "核心动作",
+          "segment_text": "制作",
+          "words": [
+            {
+              "text": "制作",
+              "score": 0.71
+            }
+          ]
+        },
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.71,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.71,
+      "scoreColor": "#22c55e"
+    },
+    "comb_制作反映人类双标_r2_32": {
+      "type": "domain_combination",
+      "query": "制作反映人类双标",
+      "level": 22,
+      "relevance_score": 0.7630792,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"制作反映人类双标\"\n【评估对象】词条\"制作反映人类双标\" vs 作用域词条\"制作反映人类双标行为的\"\n【核心动机】制作\n【动机维度 0.98】词条与原始问题的核心动机均为“制作”,动作意图完全一致。\n【品类维度 0.90】核心主体“人类双标”完全匹配,限定词“制作反映”也完全匹配。词条是同一作用域词条的截断,因此匹配度很高。\n【最终得分 0.96】\n【规则说明】规则A:动机高分保护生效(动机0.98≥0.8),实际得分0.96已≥0.7\n【加权系数计算】\n0.7982\n  来源词总得分: 0.80\n  系数: 0.80【计算公式】base_score × 系数 = 0.96 × 0.80\n【最终得分(截断后)】0.76",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+修饰短语]",
+      "source_words": [
+        [
+          "制作"
+        ],
+        [
+          "反映",
+          "人类",
+          "双标"
+        ]
+      ],
+      "from_segments": [
+        "制作",
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        1,
+        2
+      ],
+      "domains_str": "D1,D2",
+      "source_word_details": [
+        {
+          "domain_index": 1,
+          "segment_type": "核心动作",
+          "segment_text": "制作",
+          "words": [
+            {
+              "text": "制作",
+              "score": 0.71
+            }
+          ]
+        },
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.71,
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.71,
+      "scoreColor": "#22c55e"
+    },
+    "comb_制作反映人类行为_r2_33": {
+      "type": "domain_combination",
+      "query": "制作反映人类行为",
+      "level": 22,
+      "relevance_score": 0.717784,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"制作反映人类行为\"\n【评估对象】词条\"制作反映人类行为\" vs 作用域词条\"制作反映人类双标行为的\"\n【核心动机】制作\n【动机维度 0.98】原始问题和词条的核心动作都是“制作”,动作意图完全一致。\n【品类维度 0.78】核心主体'制作反映人类行为'匹配,但限定词'双标'缺失,属于核心主体匹配但限定词部分缺失的情况。\n【最终得分 0.92】\n【规则说明】规则A:动机高分保护生效(动机0.98≥0.8),实际得分0.92已≥0.7\n【加权系数计算】\n0.7802\n  来源词总得分: 0.78\n  系数: 0.78【计算公式】base_score × 系数 = 0.92 × 0.78\n【最终得分(截断后)】0.72",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+修饰短语]",
+      "source_words": [
+        [
+          "制作"
+        ],
+        [
+          "反映",
+          "人类",
+          "行为"
+        ]
+      ],
+      "from_segments": [
+        "制作",
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        1,
+        2
+      ],
+      "domains_str": "D1,D2",
+      "source_word_details": [
+        {
+          "domain_index": 1,
+          "segment_type": "核心动作",
+          "segment_text": "制作",
+          "words": [
+            {
+              "text": "制作",
+              "score": 0.71
+            }
+          ]
+        },
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.71,
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.71,
+      "scoreColor": "#22c55e"
+    },
+    "comb_制作反映双标行为_r2_34": {
+      "type": "domain_combination",
+      "query": "制作反映双标行为",
+      "level": 22,
+      "relevance_score": 0.7400964999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"制作反映双标行为\"\n【评估对象】词条\"制作反映双标行为\" vs 作用域词条\"制作反映人类双标行为的\"\n【核心动机】制作\n【动机维度 0.98】原始问题和词条的核心动作都是“制作”,动作意图完全一致。\n【品类维度 0.85】核心主体“双标行为”完全匹配,限定词“制作反映”也完全匹配。原始问题中的“人类”在词条中被省略,但由于“双标行为”通常指人类行为,因此省略不影响核心品类匹配度,属于合理泛化。\n【最终得分 0.94】\n【规则说明】规则A:动机高分保护生效(动机0.98≥0.8),实际得分0.94已≥0.7\n【加权系数计算】\n0.7865\n  来源词总得分: 0.79\n  系数: 0.79【计算公式】base_score × 系数 = 0.94 × 0.79\n【最终得分(截断后)】0.74",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+修饰短语]",
+      "source_words": [
+        [
+          "制作"
+        ],
+        [
+          "反映",
+          "双标",
+          "行为"
+        ]
+      ],
+      "from_segments": [
+        "制作",
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        1,
+        2
+      ],
+      "domains_str": "D1,D2",
+      "source_word_details": [
+        {
+          "domain_index": 1,
+          "segment_type": "核心动作",
+          "segment_text": "制作",
+          "words": [
+            {
+              "text": "制作",
+              "score": 0.71
+            }
+          ]
+        },
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.71,
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.71,
+      "scoreColor": "#22c55e"
+    },
+    "comb_制作人类双标行为_r2_35": {
+      "type": "domain_combination",
+      "query": "制作人类双标行为",
+      "level": 22,
+      "relevance_score": 0.7561959999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"制作人类双标行为\"\n【评估对象】词条\"制作人类双标行为\" vs 作用域词条\"制作反映人类双标行为的\"\n【核心动机】制作\n【动机维度 0.98】原始问题和词条的核心动作都是“制作”,语义完全一致。\n【品类维度 0.90】核心主体“人类双标行为”完全匹配,限定词“制作”也完全匹配,但原始问题中“反映”和词条中“制作”在语义上略有差异,因此给予0.9分。\n【最终得分 0.96】\n【规则说明】规则A:动机高分保护生效(动机0.98≥0.8),实际得分0.96已≥0.7\n【加权系数计算】\n0.7909999999999999\n  来源词总得分: 0.79\n  系数: 0.79【计算公式】base_score × 系数 = 0.96 × 0.79\n【最终得分(截断后)】0.76",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+修饰短语]",
+      "source_words": [
+        [
+          "制作"
+        ],
+        [
+          "人类",
+          "双标",
+          "行为"
+        ]
+      ],
+      "from_segments": [
+        "制作",
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        1,
+        2
+      ],
+      "domains_str": "D1,D2",
+      "source_word_details": [
+        {
+          "domain_index": 1,
+          "segment_type": "核心动作",
+          "segment_text": "制作",
+          "words": [
+            {
+              "text": "制作",
+              "score": 0.71
+            }
+          ]
+        },
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.71,
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.71,
+      "scoreColor": "#22c55e"
+    },
+    "comb_制作反映人类双标行为_r2_36": {
+      "type": "domain_combination",
+      "query": "制作反映人类双标行为",
+      "level": 22,
+      "relevance_score": 0.782236,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"制作反映人类双标行为\"\n【评估对象】词条\"制作反映人类双标行为\" vs 作用域词条\"制作反映人类双标行为的\"\n【核心动机】制作\n【动机维度 0.98】原始问题和词条的核心动作都是“制作”,语义完全一致。\n【品类维度 0.98】核心主体和所有关键限定词完全匹配,仅缺少一个语气助词,不影响品类匹配度。\n【最终得分 0.98】\n【规则说明】规则A:动机高分保护生效(动机0.98≥0.8),实际得分0.98已≥0.7\n【加权系数计算】\n0.7982\n  来源词总得分: 0.80\n  系数: 0.80【计算公式】base_score × 系数 = 0.98 × 0.80\n【最终得分(截断后)】0.78",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+修饰短语]",
+      "source_words": [
+        [
+          "制作"
+        ],
+        [
+          "反映",
+          "人类",
+          "双标",
+          "行为"
+        ]
+      ],
+      "from_segments": [
+        "制作",
+        "反映人类双标行为的"
+      ],
+      "domains": [
+        1,
+        2
+      ],
+      "domains_str": "D1,D2",
+      "source_word_details": [
+        {
+          "domain_index": 1,
+          "segment_type": "核心动作",
+          "segment_text": "制作",
+          "words": [
+            {
+              "text": "制作",
+              "score": 0.71
+            }
+          ]
+        },
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.71,
+        0.024,
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.71,
+      "scoreColor": "#22c55e"
+    },
+    "comb_制作猫咪_r2_37": {
+      "type": "domain_combination",
+      "query": "制作猫咪",
+      "level": 22,
+      "relevance_score": 0.576,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"制作猫咪\"\n【评估对象】词条\"制作猫咪\" vs 作用域词条\"制作猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.90】原始问题和词条的核心动作都是“制作”,动作意图高度一致。\n【品类维度 0.30】核心主体'猫咪'匹配,但限定词'表情包梗图'缺失,且'制作猫咪'的语义身份与'制作猫咪表情包梗图'中的'猫咪'不同,前者可能指制作猫咪本身(如模型、玩偶),后者指以猫咪为素材进行创作。\n【最终得分 0.72】\n【规则说明】规则A:动机高分保护生效(动机0.90≥0.8),实际得分0.72已≥0.7\n【加权系数计算】\n0.7999999999999999\n  来源词总得分: 0.80\n  系数: 0.80【计算公式】base_score × 系数 = 0.72 × 0.80\n【最终得分(截断后)】0.58",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+中心名词]",
+      "source_words": [
+        [
+          "制作"
+        ],
+        [
+          "猫咪"
+        ]
+      ],
+      "from_segments": [
+        "制作",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        1,
+        3
+      ],
+      "domains_str": "D1,D3",
+      "source_word_details": [
+        {
+          "domain_index": 1,
+          "segment_type": "核心动作",
+          "segment_text": "制作",
+          "words": [
+            {
+              "text": "制作",
+              "score": 0.71
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.71,
+        0.09
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.71,
+      "scoreColor": "#ef4444"
+    },
+    "comb_制作表情包_r2_38": {
+      "type": "domain_combination",
+      "query": "制作表情包",
+      "level": 22,
+      "relevance_score": 0.7911999999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"制作表情包\"\n【评估对象】词条\"制作表情包\" vs 作用域词条\"制作猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.98】原始问题和词条的核心动作都是“制作”,动机意图完全一致。\n【品类维度 0.78】核心主体'制作表情包'完全匹配,但限定词'猫咪'和'梗图'缺失,属于核心主体匹配,存在限定词缺失的情况。\n【最终得分 0.92】\n【规则说明】规则A:动机高分保护生效(动机0.98≥0.8),实际得分0.92已≥0.7\n【加权系数计算】\n0.86\n  来源词总得分: 0.86\n  系数: 0.86【计算公式】base_score × 系数 = 0.92 × 0.86\n【最终得分(截断后)】0.79",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+中心名词]",
+      "source_words": [
+        [
+          "制作"
+        ],
+        [
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "制作",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        1,
+        3
+      ],
+      "domains_str": "D1,D3",
+      "source_word_details": [
+        {
+          "domain_index": 1,
+          "segment_type": "核心动作",
+          "segment_text": "制作",
+          "words": [
+            {
+              "text": "制作",
+              "score": 0.71
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.71,
+        0.15
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.71,
+      "scoreColor": "#22c55e"
+    },
+    "comb_制作梗图_r2_39": {
+      "type": "domain_combination",
+      "query": "制作梗图",
+      "level": 22,
+      "relevance_score": 0.6576639999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"制作梗图\"\n【评估对象】词条\"制作梗图\" vs 作用域词条\"制作猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.98】原始问题和词条的核心动作都是“制作”,动作意图完全一致。\n【品类维度 0.70】核心主体'制作梗图'完全匹配,但限定词'猫咪表情包'缺失,属于合理泛化。\n【最终得分 0.90】\n【规则说明】规则A:动机高分保护生效(动机0.98≥0.8),实际得分0.90已≥0.7\n【加权系数计算】\n0.734\n  来源词总得分: 0.73\n  系数: 0.73【计算公式】base_score × 系数 = 0.90 × 0.73\n【最终得分(截断后)】0.66",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+中心名词]",
+      "source_words": [
+        [
+          "制作"
+        ],
+        [
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "制作",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        1,
+        3
+      ],
+      "domains_str": "D1,D3",
+      "source_word_details": [
+        {
+          "domain_index": 1,
+          "segment_type": "核心动作",
+          "segment_text": "制作",
+          "words": [
+            {
+              "text": "制作",
+              "score": 0.71
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.71,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.71,
+      "scoreColor": "#ef4444"
+    },
+    "comb_制作猫咪表情包_r2_40": {
+      "type": "domain_combination",
+      "query": "制作猫咪表情包",
+      "level": 22,
+      "relevance_score": 0.8800935999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"制作猫咪表情包\"\n【评估对象】词条\"制作猫咪表情包\" vs 作用域词条\"制作猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.98】原始问题和词条的核心动作都是“制作”,动机完全一致。\n【品类维度 0.90】核心主体“制作猫咪表情包”完全匹配,限定词“梗图”在词条中缺失,但“表情包”通常包含“梗图”的含义,属于高度相关且合理的泛化。\n【最终得分 0.96】\n【规则说明】规则A:动机高分保护生效(动机0.98≥0.8),实际得分0.96已≥0.7\n【加权系数计算】\n0.9206\n  来源词总得分: 0.92\n  系数: 0.92【计算公式】base_score × 系数 = 0.96 × 0.92\n【最终得分(截断后)】0.88",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+中心名词]",
+      "source_words": [
+        [
+          "制作"
+        ],
+        [
+          "猫咪",
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "制作",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        1,
+        3
+      ],
+      "domains_str": "D1,D3",
+      "source_word_details": [
+        {
+          "domain_index": 1,
+          "segment_type": "核心动作",
+          "segment_text": "制作",
+          "words": [
+            {
+              "text": "制作",
+              "score": 0.71
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.71,
+        0.09,
+        0.15
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.71,
+      "scoreColor": "#22c55e"
+    },
+    "comb_制作猫咪梗图_r2_41": {
+      "type": "domain_combination",
+      "query": "制作猫咪梗图",
+      "level": 22,
+      "relevance_score": 0.8800935999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"制作猫咪梗图\"\n【评估对象】词条\"制作猫咪梗图\" vs 作用域词条\"制作猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.98】原始问题和词条的核心动作都是“制作”,动机完全一致。\n【品类维度 0.90】核心主体“猫咪梗图”完全匹配,限定词“制作”也匹配。原始问题中的“表情包”是“梗图”的一种形式,属于合理泛化,因此匹配度很高。\n【最终得分 0.96】\n【规则说明】规则A:动机高分保护生效(动机0.98≥0.8),实际得分0.96已≥0.7\n【加权系数计算】\n0.9206\n  来源词总得分: 0.92\n  系数: 0.92【计算公式】base_score × 系数 = 0.96 × 0.92\n【最终得分(截断后)】0.88",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+中心名词]",
+      "source_words": [
+        [
+          "制作"
+        ],
+        [
+          "猫咪",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "制作",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        1,
+        3
+      ],
+      "domains_str": "D1,D3",
+      "source_word_details": [
+        {
+          "domain_index": 1,
+          "segment_type": "核心动作",
+          "segment_text": "制作",
+          "words": [
+            {
+              "text": "制作",
+              "score": 0.71
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.71,
+        0.09,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.71,
+      "scoreColor": "#22c55e"
+    },
+    "comb_制作表情包梗图_r2_42": {
+      "type": "domain_combination",
+      "query": "制作表情包梗图",
+      "level": 22,
+      "relevance_score": 0.8146599999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"制作表情包梗图\"\n【评估对象】词条\"制作表情包梗图\" vs 作用域词条\"制作猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 0.98】原始问题和词条的核心动作都是“制作”,动机完全一致。\n【品类维度 0.78】核心主体“制作表情包梗图”完全匹配,限定词“猫咪”缺失,属于核心主体匹配,存在限定词缺失的情况。\n【最终得分 0.92】\n【规则说明】规则A:动机高分保护生效(动机0.98≥0.8),实际得分0.92已≥0.7\n【加权系数计算】\n0.8855\n  来源词总得分: 0.89\n  系数: 0.89【计算公式】base_score × 系数 = 0.92 × 0.89\n【最终得分(截断后)】0.81",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+中心名词]",
+      "source_words": [
+        [
+          "制作"
+        ],
+        [
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "制作",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        1,
+        3
+      ],
+      "domains_str": "D1,D3",
+      "source_word_details": [
+        {
+          "domain_index": 1,
+          "segment_type": "核心动作",
+          "segment_text": "制作",
+          "words": [
+            {
+              "text": "制作",
+              "score": 0.71
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.71,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.71,
+      "scoreColor": "#22c55e"
+    },
+    "comb_制作猫咪表情包梗图_r2_43": {
+      "type": "domain_combination",
+      "query": "制作猫咪表情包梗图",
+      "level": 22,
+      "relevance_score": 0.944,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"制作猫咪表情包梗图\"\n【评估对象】词条\"制作猫咪表情包梗图\" vs 作用域词条\"制作猫咪表情包梗图\"\n【核心动机】制作\n【动机维度 1.00】原始问题和词条的核心动作都是“制作”,动作意图完全一致。\n【品类维度 1.00】核心主体“猫咪表情包梗图”和限定词“制作”均完全匹配,达到最高匹配度。\n【最终得分 1.00】\n【规则说明】规则A:动机高分保护生效(动机1.00≥0.8),实际得分1.00已≥0.7\n【加权系数计算】\n0.944\n  来源词总得分: 0.94\n  系数: 0.94【计算公式】base_score × 系数 = 1.00 × 0.94\n【最终得分(截断后)】0.94",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+中心名词]",
+      "source_words": [
+        [
+          "制作"
+        ],
+        [
+          "猫咪",
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "制作",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        1,
+        3
+      ],
+      "domains_str": "D1,D3",
+      "source_word_details": [
+        {
+          "domain_index": 1,
+          "segment_type": "核心动作",
+          "segment_text": "制作",
+          "words": [
+            {
+              "text": "制作",
+              "score": 0.71
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.71,
+        0.09,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.71,
+      "scoreColor": "#22c55e"
+    },
+    "comb_反映猫咪_r2_44": {
+      "type": "domain_combination",
+      "query": "反映猫咪",
+      "level": 22,
+      "relevance_score": 0.006839999999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映猫咪\"\n【评估对象】词条\"反映猫咪\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】词条“反映猫咪”无明确动作意图,无法评估动作匹配度。\n【品类维度 0.20】词条“反映猫咪”仅包含原始问题中的核心主体“猫咪”,但缺失了“人类双标行为”、“表情包”、“梗图”等关键限定词,导致品类匹配度较低。\n【最终得分 0.06】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.06已≤0.5\n【加权系数计算】\n0.11399999999999999\n  来源词总得分: 0.11\n  系数: 0.11【计算公式】base_score × 系数 = 0.06 × 0.11\n【最终得分(截断后)】0.01",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映"
+        ],
+        [
+          "猫咪"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.09
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.09,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映表情包_r2_45": {
+      "type": "domain_combination",
+      "query": "反映表情包",
+      "level": 22,
+      "relevance_score": 0.004176,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映表情包\"\n【评估对象】词条\"反映表情包\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看/了解\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动作匹配度。\n【品类维度 0.08】词条“反映表情包”是通用概念,而同一作用域词条“反映人类双标行为的猫咪表情包梗图”是特定概念。词条仅包含“反映”和“表情包”两个词,无法体现出“人类双标行为”、“猫咪”或“梗图”等特定限定词,因此品类匹配度低,但由于都包含“表情包”这一核心词,所以给予较低的正分。\n【最终得分 0.02】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.02已≤0.5\n【加权系数计算】\n0.174\n  来源词总得分: 0.17\n  系数: 0.17【计算公式】base_score × 系数 = 0.02 × 0.17\n【最终得分(截断后)】0.00",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映"
+        ],
+        [
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.15
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映梗图_r2_46": {
+      "type": "domain_combination",
+      "query": "反映梗图",
+      "level": 22,
+      "relevance_score": 0.001152,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映梗图\"\n【评估对象】词条\"反映梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看/制作\n【动机维度 0.00】原始问题意图是寻找或制作关于特定主题的表情包梗图,而词条“反映梗图”没有明确的动作意图,无法评估动机匹配度。\n【品类维度 0.08】词条“反映梗图”是通用概念,而同一作用域词条“反映人类双标行为的猫咪表情包梗图”是特定概念。虽然都包含“反映”和“梗图”,但词条缺少了核心主体“人类双标行为”、“猫咪表情包”等关键限定词,属于过度泛化,因此品类匹配度低。\n【最终得分 0.02】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.02已≤0.5\n【加权系数计算】\n0.048\n  来源词总得分: 0.05\n  系数: 0.05【计算公式】base_score × 系数 = 0.02 × 0.05\n【最终得分(截断后)】0.00",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映"
+        ],
+        [
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.024,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映猫咪表情包_r2_47": {
+      "type": "domain_combination",
+      "query": "反映猫咪表情包",
+      "level": 22,
+      "relevance_score": 0.05278499999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映猫咪表情包\"\n【评估对象】词条\"反映猫咪表情包\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条都无明确的动作意图,无法评估动作匹配度。\n【品类维度 0.75】核心主体“猫咪表情包”匹配,但限定词“人类双标行为”和“梗图”缺失,属于核心主体匹配但限定词不完全匹配的情况。\n【最终得分 0.22】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.22已≤0.5\n【加权系数计算】\n0.23459999999999998\n  来源词总得分: 0.23\n  系数: 0.23【计算公式】base_score × 系数 = 0.22 × 0.23\n【最终得分(截断后)】0.05",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映"
+        ],
+        [
+          "猫咪",
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.09,
+        0.15
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映猫咪梗图_r2_48": {
+      "type": "domain_combination",
+      "query": "反映猫咪梗图",
+      "level": 22,
+      "relevance_score": 0.05489639999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映猫咪梗图\"\n【评估对象】词条\"反映猫咪梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确动作意图,无法评估动作匹配度。\n【品类维度 0.78】核心主体'猫咪梗图'匹配,但限定词'人类双标行为'缺失,'表情包'缺失,属于核心主体匹配,存在限定词缺失的情况。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.23459999999999998\n  来源词总得分: 0.23\n  系数: 0.23【计算公式】base_score × 系数 = 0.23 × 0.23\n【最终得分(截断后)】0.05",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映"
+        ],
+        [
+          "猫咪",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.09,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.09,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映表情包梗图_r2_49": {
+      "type": "domain_combination",
+      "query": "反映表情包梗图",
+      "level": 22,
+      "relevance_score": 0.004788,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映表情包梗图\"\n【评估对象】词条\"反映表情包梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.08】原始问题是特定概念“反映人类双标行为的猫咪表情包梗图”,而词条“反映表情包梗图”是通用概念,缺少了核心主体“人类双标行为”和“猫咪”这两个关键限定词,因此品类匹配度低。\n【最终得分 0.02】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.02已≤0.5\n【加权系数计算】\n0.19949999999999998\n  来源词总得分: 0.20\n  系数: 0.20【计算公式】base_score × 系数 = 0.02 × 0.20\n【最终得分(截断后)】0.00",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映"
+        ],
+        [
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映猫咪表情包梗图_r2_50": {
+      "type": "domain_combination",
+      "query": "反映猫咪表情包梗图",
+      "level": 22,
+      "relevance_score": 0.05805,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映猫咪表情包梗图\"\n【评估对象】词条\"反映猫咪表情包梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看/了解\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.75】核心主体“猫咪表情包梗图”匹配,但限定词“反映人类双标行为”缺失。\n【最终得分 0.22】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.22已≤0.5\n【加权系数计算】\n0.258\n  来源词总得分: 0.26\n  系数: 0.26【计算公式】base_score × 系数 = 0.22 × 0.26\n【最终得分(截断后)】0.06",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映"
+        ],
+        [
+          "猫咪",
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.09,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_人类猫咪_r2_51": {
+      "type": "domain_combination",
+      "query": "人类猫咪",
+      "level": 22,
+      "relevance_score": 0.0027359999999999997,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"人类猫咪\"\n【评估对象】词条\"人类猫咪\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】词条“人类猫咪”无明确的动作意图,无法与原始问题的“寻找/查看”动作进行匹配。\n【品类维度 0.08】原始问题是特定概念(反映人类双标行为的猫咪表情包梗图),而词条是通用概念(人类猫咪),词条过度泛化,仅抽象相似,因此品类匹配度低。\n【最终得分 0.02】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.02已≤0.5\n【加权系数计算】\n0.11399999999999999\n  来源词总得分: 0.11\n  系数: 0.11【计算公式】base_score × 系数 = 0.02 × 0.11\n【最终得分(截断后)】0.00",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "人类"
+        ],
+        [
+          "猫咪"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.09
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.09,
+      "scoreColor": "#ef4444"
+    },
+    "comb_人类表情包_r2_52": {
+      "type": "domain_combination",
+      "query": "人类表情包",
+      "level": 22,
+      "relevance_score": 0.004176,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"人类表情包\"\n【评估对象】词条\"人类表情包\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/获取\n【动机维度 0.00】原始问题是寻找/获取表情包梗图,而词条“人类表情包”无明确的动作意图,无法评估动作匹配度。\n【品类维度 0.08】原始问题是关于“猫咪表情包梗图”的特定品类,而词条“人类表情包”是一个通用概念,虽然都包含“表情包”,但主体“人类”与原始问题的“猫咪”不匹配,且词条过于泛化,因此品类匹配度低。\n【最终得分 0.02】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.02已≤0.5\n【加权系数计算】\n0.174\n  来源词总得分: 0.17\n  系数: 0.17【计算公式】base_score × 系数 = 0.02 × 0.17\n【最终得分(截断后)】0.00",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "人类"
+        ],
+        [
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.15
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_人类梗图_r2_53": {
+      "type": "domain_combination",
+      "query": "人类梗图",
+      "level": 22,
+      "relevance_score": 0.001152,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"人类梗图\"\n【评估对象】词条\"人类梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.08】原始问题是特定概念“反映人类双标行为的猫咪表情包梗图”,sug词是通用概念“人类梗图”。sug词过度泛化,仅抽象相似,品类不匹配。\n【最终得分 0.02】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.02已≤0.5\n【加权系数计算】\n0.048\n  来源词总得分: 0.05\n  系数: 0.05【计算公式】base_score × 系数 = 0.02 × 0.05\n【最终得分(截断后)】0.00",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "人类"
+        ],
+        [
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.024,
+      "scoreColor": "#ef4444"
+    },
+    "comb_人类猫咪表情包_r2_54": {
+      "type": "domain_combination",
+      "query": "人类猫咪表情包",
+      "level": 22,
+      "relevance_score": 0.04926599999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"人类猫咪表情包\"\n【评估对象】词条\"人类猫咪表情包\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/获取\n【动机维度 0.00】原始问题是寻找/获取表情包梗图,而词条“人类猫咪表情包”没有明确的动作意图,无法评估动机匹配度。\n【品类维度 0.70】核心主体“猫咪表情包”匹配,但限定词“反映人类双标行为”未匹配,且“梗图”也未匹配,属于核心主体匹配但限定词缺失的情况。\n【最终得分 0.21】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.21已≤0.5\n【加权系数计算】\n0.23459999999999998\n  来源词总得分: 0.23\n  系数: 0.23【计算公式】base_score × 系数 = 0.21 × 0.23\n【最终得分(截断后)】0.05",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "人类"
+        ],
+        [
+          "猫咪",
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.09,
+        0.15
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_人类猫咪梗图_r2_55": {
+      "type": "domain_combination",
+      "query": "人类猫咪梗图",
+      "level": 22,
+      "relevance_score": 0.05278499999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"人类猫咪梗图\"\n【评估对象】词条\"人类猫咪梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.75】核心主体'猫咪梗图'匹配,限定词'人类'匹配,但缺失了'双标行为'和'表情包'这两个限定词,属于核心主体匹配,存在限定词匹配的情况。\n【最终得分 0.22】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.22已≤0.5\n【加权系数计算】\n0.23459999999999998\n  来源词总得分: 0.23\n  系数: 0.23【计算公式】base_score × 系数 = 0.22 × 0.23\n【最终得分(截断后)】0.05",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "人类"
+        ],
+        [
+          "猫咪",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.09,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.09,
+      "scoreColor": "#ef4444"
+    },
+    "comb_人类表情包梗图_r2_56": {
+      "type": "domain_combination",
+      "query": "人类表情包梗图",
+      "level": 22,
+      "relevance_score": 0.029924999999999997,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"人类表情包梗图\"\n【评估对象】词条\"人类表情包梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】原始问题无明确动作意图,主要描述一个主题或内容\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.50】核心主体'表情包梗图'匹配,但限定词'人类'与原始问题中的'猫咪'存在差异,且原始问题中的'双标行为'这一限定词未匹配。\n【最终得分 0.15】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.15已≤0.5\n【加权系数计算】\n0.19949999999999998\n  来源词总得分: 0.20\n  系数: 0.20【计算公式】base_score × 系数 = 0.15 × 0.20\n【最终得分(截断后)】0.03",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "人类"
+        ],
+        [
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_人类猫咪表情包梗图_r2_57": {
+      "type": "domain_combination",
+      "query": "人类猫咪表情包梗图",
+      "level": 22,
+      "relevance_score": 0.060371999999999995,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"人类猫咪表情包梗图\"\n【评估对象】词条\"人类猫咪表情包梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】原始问题无明确动作意图,主要描述一个主题\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.78】核心主体“猫咪表情包梗图”完全匹配,限定词“人类”匹配,但缺失了“双标行为”这一关键限定词,因此给予较高但非满分。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.258\n  来源词总得分: 0.26\n  系数: 0.26【计算公式】base_score × 系数 = 0.23 × 0.26\n【最终得分(截断后)】0.06",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "人类"
+        ],
+        [
+          "猫咪",
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.09,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_双标猫咪_r2_58": {
+      "type": "domain_combination",
+      "query": "双标猫咪",
+      "level": 22,
+      "relevance_score": 0.026675999999999995,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"双标猫咪\"\n【评估对象】词条\"双标猫咪\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】原始问题无明确动作意图,主要为描述性词语\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机维度匹配度。\n【品类维度 0.78】核心主体'双标'和'猫咪'匹配,但限定词'行为'、'表情包'、'梗图'缺失,属于核心主体匹配,存在限定词匹配的情况。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.11399999999999999\n  来源词总得分: 0.11\n  系数: 0.11【计算公式】base_score × 系数 = 0.23 × 0.11\n【最终得分(截断后)】0.03",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "双标"
+        ],
+        [
+          "猫咪"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.09
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.09,
+      "scoreColor": "#ef4444"
+    },
+    "comb_双标表情包_r2_59": {
+      "type": "domain_combination",
+      "query": "双标表情包",
+      "level": 22,
+      "relevance_score": 0.040715999999999995,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"双标表情包\"\n【评估对象】词条\"双标表情包\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/获取\n【动机维度 0.00】原始问题是寻找/获取表情包梗图,而词条“双标表情包”没有明确的动作意图,无法进行动机匹配。\n【品类维度 0.78】核心主体“双标”和“表情包”匹配,但缺失了“人类行为”、“猫咪”和“梗图”等限定词,属于核心主体匹配,存在限定词匹配的情况。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.174\n  来源词总得分: 0.17\n  系数: 0.17【计算公式】base_score × 系数 = 0.23 × 0.17\n【最终得分(截断后)】0.04",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "双标"
+        ],
+        [
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.15
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_双标梗图_r2_60": {
+      "type": "domain_combination",
+      "query": "双标梗图",
+      "level": 22,
+      "relevance_score": 0.010799999999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"双标梗图\"\n【评估对象】词条\"双标梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/获取\n【动机维度 0.00】原始问题是寻找/获取梗图,但词条本身没有明确的动作意图,无法评估动机匹配度。\n【品类维度 0.75】核心主体“双标梗图”匹配,但限定词“人类双标行为的猫咪表情包”缺失,属于核心主体匹配但限定词不完全匹配的情况。\n【最终得分 0.22】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.22已≤0.5\n【加权系数计算】\n0.048\n  来源词总得分: 0.05\n  系数: 0.05【计算公式】base_score × 系数 = 0.22 × 0.05\n【最终得分(截断后)】0.01",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "双标"
+        ],
+        [
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.024,
+      "scoreColor": "#ef4444"
+    },
+    "comb_双标猫咪表情包_r2_61": {
+      "type": "domain_combination",
+      "query": "双标猫咪表情包",
+      "level": 22,
+      "relevance_score": 0.059822999999999994,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"双标猫咪表情包\"\n【评估对象】词条\"双标猫咪表情包\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】原始问题无明确动作意图,主要描述一个主题或内容\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.85】核心主体'双标猫咪表情包'与原始问题中的'双标行为的猫咪表情包梗图'高度匹配,限定词'双标'和'猫咪表情包'均包含,仅缺少'梗图'这一细微限定,但语义上高度一致。\n【最终得分 0.26】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.26已≤0.5\n【加权系数计算】\n0.23459999999999998\n  来源词总得分: 0.23\n  系数: 0.23【计算公式】base_score × 系数 = 0.26 × 0.23\n【最终得分(截断后)】0.06",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "双标"
+        ],
+        [
+          "猫咪",
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.09,
+        0.15
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_双标猫咪梗图_r2_62": {
+      "type": "domain_combination",
+      "query": "双标猫咪梗图",
+      "level": 22,
+      "relevance_score": 0.063342,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"双标猫咪梗图\"\n【评估对象】词条\"双标猫咪梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】原始问题无明确动作意图,主要描述一个主题\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.90】核心主体“双标猫咪梗图”与原始问题“反映人类双标行为的猫咪表情包梗图”高度匹配,限定词“双标”、“猫咪”、“梗图”均包含,仅缺少“表情包”和“反映人类行为”的限定,但语义上高度一致。\n【最终得分 0.27】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.27已≤0.5\n【加权系数计算】\n0.23459999999999998\n  来源词总得分: 0.23\n  系数: 0.23【计算公式】base_score × 系数 = 0.27 × 0.23\n【最终得分(截断后)】0.06",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "双标"
+        ],
+        [
+          "猫咪",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.09,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.09,
+      "scoreColor": "#ef4444"
+    },
+    "comb_双标表情包梗图_r2_63": {
+      "type": "domain_combination",
+      "query": "双标表情包梗图",
+      "level": 22,
+      "relevance_score": 0.046682999999999995,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"双标表情包梗图\"\n【评估对象】词条\"双标表情包梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】原始问题无明确动作意图,主要描述一个对象或主题\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.78】核心主体“双标行为”和“表情包梗图”匹配,但限定词“猫咪”缺失,在词条中缺失,属于部分匹配。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.19949999999999998\n  来源词总得分: 0.20\n  系数: 0.20【计算公式】base_score × 系数 = 0.23 × 0.20\n【最终得分(截断后)】0.05",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "双标"
+        ],
+        [
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_双标猫咪表情包梗图_r2_64": {
+      "type": "domain_combination",
+      "query": "双标猫咪表情包梗图",
+      "level": 22,
+      "relevance_score": 0.06966,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"双标猫咪表情包梗图\"\n【评估对象】词条\"双标猫咪表情包梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.90】核心主体“猫咪表情包梗图”和关键限定词“双标”完全匹配,仅缺少“反映人类行为”这一修饰性限定词,但其核心品类意图高度一致。\n【最终得分 0.27】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.27已≤0.5\n【加权系数计算】\n0.258\n  来源词总得分: 0.26\n  系数: 0.26【计算公式】base_score × 系数 = 0.27 × 0.26\n【最终得分(截断后)】0.07",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "双标"
+        ],
+        [
+          "猫咪",
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.09,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_行为猫咪_r2_65": {
+      "type": "domain_combination",
+      "query": "行为猫咪",
+      "level": 22,
+      "relevance_score": 0.0027359999999999997,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"行为猫咪\"\n【评估对象】词条\"行为猫咪\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作/寻找\n【动机维度 0.00】原始问题意图是制作或寻找表情包梗图,而词条“行为猫咪”无明确动作意图,无法评估动作匹配度。\n【品类维度 0.08】原始问题是特定概念“反映人类双标行为的猫咪表情包梗图”,而词条“行为猫咪”是通用概念。词条仅包含原始问题中的部分主体词“行为”和“猫咪”,但缺失了所有限定词,且“行为猫咪”本身是一个泛化的概念,无法直接匹配原始问题的具体品类,因此品类匹配度低。\n【最终得分 0.02】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.02已≤0.5\n【加权系数计算】\n0.11399999999999999\n  来源词总得分: 0.11\n  系数: 0.11【计算公式】base_score × 系数 = 0.02 × 0.11\n【最终得分(截断后)】0.00",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "行为"
+        ],
+        [
+          "猫咪"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.09
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.09,
+      "scoreColor": "#ef4444"
+    },
+    "comb_行为表情包_r2_66": {
+      "type": "domain_combination",
+      "query": "行为表情包",
+      "level": 22,
+      "relevance_score": 0.004176,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"行为表情包\"\n【评估对象】词条\"行为表情包\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/获取\n【动机维度 0.00】原始问题是寻找/获取表情包梗图,而词条“行为表情包”本身没有明确的动作意图,无法评估动机匹配度。\n【品类维度 0.08】原始问题是特定概念“反映人类双标行为的猫咪表情包梗图”,sug词“行为表情包”是通用概念,虽然包含“行为”和“表情包”,但缺少了核心主体“猫咪”和限定词“双标行为”,属于主体词过度泛化或仅抽象相似,因此评分较低。\n【最终得分 0.02】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.02已≤0.5\n【加权系数计算】\n0.174\n  来源词总得分: 0.17\n  系数: 0.17【计算公式】base_score × 系数 = 0.02 × 0.17\n【最终得分(截断后)】0.00",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "行为"
+        ],
+        [
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.15
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_行为梗图_r2_67": {
+      "type": "domain_combination",
+      "query": "行为梗图",
+      "level": 22,
+      "relevance_score": 0.001152,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"行为梗图\"\n【评估对象】词条\"行为梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/获取\n【动机维度 0.00】词条“行为梗图”无明确动作意图,无法评估动作匹配度。\n【品类维度 0.08】原始问题是特定概念“反映人类双标行为的猫咪表情包梗图”,sug词是通用概念“行为梗图”,通用概念不等于特定概念,因此品类不匹配。\n【最终得分 0.02】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.02已≤0.5\n【加权系数计算】\n0.048\n  来源词总得分: 0.05\n  系数: 0.05【计算公式】base_score × 系数 = 0.02 × 0.05\n【最终得分(截断后)】0.00",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "行为"
+        ],
+        [
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.024,
+      "scoreColor": "#ef4444"
+    },
+    "comb_行为猫咪表情包_r2_68": {
+      "type": "domain_combination",
+      "query": "行为猫咪表情包",
+      "level": 22,
+      "relevance_score": 0.024632999999999995,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"行为猫咪表情包\"\n【评估对象】词条\"行为猫咪表情包\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/获取\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.35】核心主体'猫咪表情包'匹配,但限定词'双标行为'和'梗图'缺失,且'行为'作为限定词与原始问题中的'行为'语义身份不同,原始问题中'行为'是猫咪的动作,而词条中'行为'是修饰猫咪表情包的类型。\n【最终得分 0.10】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.10已≤0.5\n【加权系数计算】\n0.23459999999999998\n  来源词总得分: 0.23\n  系数: 0.23【计算公式】base_score × 系数 = 0.10 × 0.23\n【最终得分(截断后)】0.02",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "行为"
+        ],
+        [
+          "猫咪",
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.09,
+        0.15
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_行为猫咪梗图_r2_69": {
+      "type": "domain_combination",
+      "query": "行为猫咪梗图",
+      "level": 22,
+      "relevance_score": 0.05489639999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"行为猫咪梗图\"\n【评估对象】词条\"行为猫咪梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.78】核心主体'猫咪梗图'匹配,限定词'行为'部分匹配了原始问题中的'双标行为',但缺失了'双标'和'表情包'的限定。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.23459999999999998\n  来源词总得分: 0.23\n  系数: 0.23【计算公式】base_score × 系数 = 0.23 × 0.23\n【最终得分(截断后)】0.05",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "行为"
+        ],
+        [
+          "猫咪",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.09,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.09,
+      "scoreColor": "#ef4444"
+    },
+    "comb_行为表情包梗图_r2_70": {
+      "type": "domain_combination",
+      "query": "行为表情包梗图",
+      "level": 22,
+      "relevance_score": 0.032917499999999995,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"行为表情包梗图\"\n【评估对象】词条\"行为表情包梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/获取\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.55】核心主体'表情包梗图'匹配,限定词'行为'部分匹配了原始问题中的'双标行为',但缺失了'人类'和'猫咪'这两个关键限定词,导致匹配度中等偏上。\n【最终得分 0.17】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.17已≤0.5\n【加权系数计算】\n0.19949999999999998\n  来源词总得分: 0.20\n  系数: 0.20【计算公式】base_score × 系数 = 0.17 × 0.20\n【最终得分(截断后)】0.03",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "行为"
+        ],
+        [
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_行为猫咪表情包梗图_r2_71": {
+      "type": "domain_combination",
+      "query": "行为猫咪表情包梗图",
+      "level": 22,
+      "relevance_score": 0.05805,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"行为猫咪表情包梗图\"\n【评估对象】词条\"行为猫咪表情包梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.75】核心主体“猫咪表情包梗图”匹配,限定词“行为”匹配“双标行为”中的“行为”,但缺失“双标”这一关键限定词。\n【最终得分 0.22】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.22已≤0.5\n【加权系数计算】\n0.258\n  来源词总得分: 0.26\n  系数: 0.26【计算公式】base_score × 系数 = 0.22 × 0.26\n【最终得分(截断后)】0.06",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "行为"
+        ],
+        [
+          "猫咪",
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.09,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映人类猫咪_r2_72": {
+      "type": "domain_combination",
+      "query": "反映人类猫咪",
+      "level": 22,
+      "relevance_score": 0.014418,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映人类猫咪\"\n【评估对象】词条\"反映人类猫咪\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题是寻找/查看表情包梗图,而词条无明确的动作意图,无法评估动作匹配度。\n【品类维度 0.30】词条“反映人类猫咪”与同一作用域词条“反映人类双标行为的猫咪表情包梗图”在核心主体“反映人类”和“猫咪”上有所匹配,但缺失了“双标行为”、“表情包”、“梗图”等关键限定词,导致品类匹配度较低。\n【最终得分 0.09】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.09已≤0.5\n【加权系数计算】\n0.1602\n  来源词总得分: 0.16\n  系数: 0.16【计算公式】base_score × 系数 = 0.09 × 0.16\n【最终得分(截断后)】0.01",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "人类"
+        ],
+        [
+          "猫咪"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.09
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.09,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映人类表情包_r2_73": {
+      "type": "domain_combination",
+      "query": "反映人类表情包",
+      "level": 22,
+      "relevance_score": 0.023121,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映人类表情包\"\n【评估对象】词条\"反映人类表情包\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/获取\n【动机维度 0.00】原始问题是寻找/获取表情包,而词条“反映人类表情包”没有明确的动作意图,无法评估动作匹配度。\n【品类维度 0.35】核心主体'表情包'匹配,但限定词'人类双标行为'和'猫咪'缺失,且词条'反映人类表情包'的语义身份与原始问题中'反映人类双标行为的猫咪表情包梗图'的语义身份不同,原始问题中'人类'是行为者,词条中'人类'是表情包的限定词。\n【最终得分 0.10】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.10已≤0.5\n【加权系数计算】\n0.2202\n  来源词总得分: 0.22\n  系数: 0.22【计算公式】base_score × 系数 = 0.10 × 0.22\n【最终得分(截断后)】0.02",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "人类"
+        ],
+        [
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.15
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映人类梗图_r2_74": {
+      "type": "domain_combination",
+      "query": "反映人类梗图",
+      "level": 22,
+      "relevance_score": 0.009891,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映人类梗图\"\n【评估对象】词条\"反映人类梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作/寻找/理解\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.35】核心主体'梗图'匹配,限定词'反映人类'匹配,但缺失了'双标行为'和'猫咪表情包'这两个重要的限定词,导致匹配度中等偏低。\n【最终得分 0.10】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.10已≤0.5\n【加权系数计算】\n0.0942\n  来源词总得分: 0.09\n  系数: 0.09【计算公式】base_score × 系数 = 0.10 × 0.09\n【最终得分(截断后)】0.01",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "人类"
+        ],
+        [
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.024,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映人类猫咪表情包_r2_75": {
+      "type": "domain_combination",
+      "query": "反映人类猫咪表情包",
+      "level": 22,
+      "relevance_score": 0.025272,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映人类猫咪表情包\"\n【评估对象】词条\"反映人类猫咪表情包\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.30】核心主体'猫咪表情包'匹配,但限定词'反映人类双标行为'完全缺失,且'反映人类'这个限定词在词条中被泛化,导致语义身份发生变化。\n【最终得分 0.09】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.09已≤0.5\n【加权系数计算】\n0.2808\n  来源词总得分: 0.28\n  系数: 0.28【计算公式】base_score × 系数 = 0.09 × 0.28\n【最终得分(截断后)】0.03",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "人类"
+        ],
+        [
+          "猫咪",
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.09,
+        0.15
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映人类猫咪梗图_r2_76": {
+      "type": "domain_combination",
+      "query": "反映人类猫咪梗图",
+      "level": 22,
+      "relevance_score": 0.058968,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映人类猫咪梗图\"\n【评估对象】词条\"反映人类猫咪梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.70】核心主体'人类猫咪梗图'匹配,但限定词'双标行为'和'表情包'缺失,导致匹配度下降。\n【最终得分 0.21】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.21已≤0.5\n【加权系数计算】\n0.2808\n  来源词总得分: 0.28\n  系数: 0.28【计算公式】base_score × 系数 = 0.21 × 0.28\n【最终得分(截断后)】0.06",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "人类"
+        ],
+        [
+          "猫咪",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.09,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.09,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映人类表情包梗图_r2_77": {
+      "type": "domain_combination",
+      "query": "反映人类表情包梗图",
+      "level": 22,
+      "relevance_score": 0.05528249999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映人类表情包梗图\"\n【评估对象】词条\"反映人类表情包梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.75】核心主体“表情包梗图”匹配,限定词“反映人类”匹配,但缺失了“双标行为”和“猫咪”这两个限定词,属于核心主体匹配,存在部分限定词匹配的情况。\n【最终得分 0.22】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.22已≤0.5\n【加权系数计算】\n0.24569999999999997\n  来源词总得分: 0.25\n  系数: 0.25【计算公式】base_score × 系数 = 0.22 × 0.25\n【最终得分(截断后)】0.06",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "人类"
+        ],
+        [
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映人类猫咪表情包梗图_r2_78": {
+      "type": "domain_combination",
+      "query": "反映人类猫咪表情包梗图",
+      "level": 22,
+      "relevance_score": 0.06844499999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映人类猫咪表情包梗图\"\n【评估对象】词条\"反映人类猫咪表情包梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】无明确动作意图\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.75】核心主体“人类猫咪表情包梗图”匹配,但限定词“双标行为”缺失,属于核心主体匹配但限定词不完全匹配的情况。\n【最终得分 0.22】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.22已≤0.5\n【加权系数计算】\n0.30419999999999997\n  来源词总得分: 0.30\n  系数: 0.30【计算公式】base_score × 系数 = 0.22 × 0.30\n【最终得分(截断后)】0.07",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "人类"
+        ],
+        [
+          "猫咪",
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.09,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映双标猫咪_r2_79": {
+      "type": "domain_combination",
+      "query": "反映双标猫咪",
+      "level": 22,
+      "relevance_score": 0.040013999999999994,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映双标猫咪\"\n【评估对象】词条\"反映双标猫咪\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】原始问题无明确动作意图,主要描述一个主题或内容\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.78】核心主体'双标猫咪'匹配,但缺失了限定词'表情包梗图',属于核心主体匹配但限定词不完全匹配的情况。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.17099999999999999\n  来源词总得分: 0.17\n  系数: 0.17【计算公式】base_score × 系数 = 0.23 × 0.17\n【最终得分(截断后)】0.04",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "双标"
+        ],
+        [
+          "猫咪"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.09
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.09,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映双标表情包_r2_80": {
+      "type": "domain_combination",
+      "query": "反映双标表情包",
+      "level": 22,
+      "relevance_score": 0.05405399999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映双标表情包\"\n【评估对象】词条\"反映双标表情包\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】制作/寻找/理解\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.78】核心主体“双标表情包”匹配,但缺失了限定词“人类行为”和“猫咪”\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.23099999999999998\n  来源词总得分: 0.23\n  系数: 0.23【计算公式】base_score × 系数 = 0.23 × 0.23\n【最终得分(截断后)】0.05",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "双标"
+        ],
+        [
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.15
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映双标梗图_r2_81": {
+      "type": "domain_combination",
+      "query": "反映双标梗图",
+      "level": 22,
+      "relevance_score": 0.02457,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映双标梗图\"\n【评估对象】词条\"反映双标梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.78】核心主体'双标梗图'匹配,但缺失了限定词'人类行为'和'猫咪表情包',属于核心主体匹配,存在限定词匹配的情况。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.10500000000000001\n  来源词总得分: 0.11\n  系数: 0.11【计算公式】base_score × 系数 = 0.23 × 0.11\n【最终得分(截断后)】0.02",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "双标"
+        ],
+        [
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.024,
+      "scoreColor": "#22c55e"
+    },
+    "comb_反映双标猫咪表情包_r2_82": {
+      "type": "domain_combination",
+      "query": "反映双标猫咪表情包",
+      "level": 22,
+      "relevance_score": 0.074358,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映双标猫咪表情包\"\n【评估对象】词条\"反映双标猫咪表情包\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/获取\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.85】核心主体“双标猫咪表情包”匹配,限定词“反映”匹配,但缺失了“人类行为”和“梗图”这两个限定词,因此给予较高但非满分评分。\n【最终得分 0.26】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.26已≤0.5\n【加权系数计算】\n0.29159999999999997\n  来源词总得分: 0.29\n  系数: 0.29【计算公式】base_score × 系数 = 0.26 × 0.29\n【最终得分(截断后)】0.07",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "双标"
+        ],
+        [
+          "猫咪",
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.09,
+        0.15
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映双标猫咪梗图_r2_83": {
+      "type": "domain_combination",
+      "query": "反映双标猫咪梗图",
+      "level": 22,
+      "relevance_score": 0.27439559999999996,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映双标猫咪梗图\"\n【评估对象】词条\"反映双标猫咪梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.98】原始问题和词条都隐含了“寻找/查看”的动机,词条是原始问题动机的同义表达,动机完全一致。\n【品类维度 0.85】核心主体“双标猫咪梗图”完全匹配,限定词“反映”匹配,但缺失了“人类行为”这一限定词,因此给予高分但非满分。\n【最终得分 0.94】\n【规则说明】规则A:动机高分保护生效(动机0.98≥0.8),实际得分0.94已≥0.7\n【加权系数计算】\n0.29159999999999997\n  来源词总得分: 0.29\n  系数: 0.29【计算公式】base_score × 系数 = 0.94 × 0.29\n【最终得分(截断后)】0.27",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "双标"
+        ],
+        [
+          "猫咪",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.09,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.09,
+      "scoreColor": "#22c55e"
+    },
+    "comb_反映双标表情包梗图_r2_84": {
+      "type": "domain_combination",
+      "query": "反映双标表情包梗图",
+      "level": 22,
+      "relevance_score": 0.060021,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映双标表情包梗图\"\n【评估对象】词条\"反映双标表情包梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.78】核心主体“双标表情包梗图”完全匹配,但缺失了限定词“人类”和“猫咪”,属于核心主体匹配但限定词不完全匹配的情况。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.2565\n  来源词总得分: 0.26\n  系数: 0.26【计算公式】base_score × 系数 = 0.23 × 0.26\n【最终得分(截断后)】0.06",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "双标"
+        ],
+        [
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映双标猫咪表情包梗图_r2_85": {
+      "type": "domain_combination",
+      "query": "反映双标猫咪表情包梗图",
+      "level": 22,
+      "relevance_score": 0.08505,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映双标猫咪表情包梗图\"\n【评估对象】词条\"反映双标猫咪表情包梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.90】核心主体'双标猫咪表情包梗图'完全匹配,但限定词'人类行为'在词条中缺失,属于限定词部分匹配。\n【最终得分 0.27】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.27已≤0.5\n【加权系数计算】\n0.315\n  来源词总得分: 0.32\n  系数: 0.32【计算公式】base_score × 系数 = 0.27 × 0.32\n【最终得分(截断后)】0.09",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "双标"
+        ],
+        [
+          "猫咪",
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.09,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映行为猫咪_r2_86": {
+      "type": "domain_combination",
+      "query": "反映行为猫咪",
+      "level": 22,
+      "relevance_score": 0.010206,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映行为猫咪\"\n【评估对象】词条\"反映行为猫咪\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题是寻找/查看表情包梗图,词条“反映行为猫咪”无明确动作意图,无法评估动作匹配度。\n【品类维度 0.35】核心主体'猫咪'和限定词'反映行为'匹配,但缺失了'双标'、'表情包'、'梗图'等关键限定词,导致语义不完整。\n【最终得分 0.10】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.10已≤0.5\n【加权系数计算】\n0.0972\n  来源词总得分: 0.10\n  系数: 0.10【计算公式】base_score × 系数 = 0.10 × 0.10\n【最终得分(截断后)】0.01",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "行为"
+        ],
+        [
+          "猫咪"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.09
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.09,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映行为表情包_r2_87": {
+      "type": "domain_combination",
+      "query": "反映行为表情包",
+      "level": 22,
+      "relevance_score": 0.016506,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映行为表情包\"\n【评估对象】词条\"反映行为表情包\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.35】核心主体'表情包'和限定词'反映行为'匹配,但缺失了关键限定词'人类双标'和主体限定词'猫咪',导致匹配度降低。\n【最终得分 0.10】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.10已≤0.5\n【加权系数计算】\n0.1572\n  来源词总得分: 0.16\n  系数: 0.16【计算公式】base_score × 系数 = 0.10 × 0.16\n【最终得分(截断后)】0.02",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "行为"
+        ],
+        [
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.15
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映行为梗图_r2_88": {
+      "type": "domain_combination",
+      "query": "反映行为梗图",
+      "level": 22,
+      "relevance_score": 0.0007488,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映行为梗图\"\n【评估对象】词条\"反映行为梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.08】词条“反映行为梗图”是通用概念,而同一作用域词条“反映人类双标行为的猫咪表情包梗图”是特定概念。词条仅包含“行为梗图”,缺失了核心主体“人类双标行为”和限定词“猫咪表情包”,因此品类匹配度低。\n【最终得分 0.02】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.02已≤0.5\n【加权系数计算】\n0.0312\n  来源词总得分: 0.03\n  系数: 0.03【计算公式】base_score × 系数 = 0.02 × 0.03\n【最终得分(截断后)】0.00",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "行为"
+        ],
+        [
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.024,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映行为猫咪表情包_r2_89": {
+      "type": "domain_combination",
+      "query": "反映行为猫咪表情包",
+      "level": 22,
+      "relevance_score": 0.050965199999999995,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映行为猫咪表情包\"\n【评估对象】词条\"反映行为猫咪表情包\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/获取\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.78】核心主体'猫咪表情包'匹配,限定词'反映行为'部分匹配了原始问题中的'反映人类双标行为',但缺失了'双标'和'梗图'这两个限定词。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.2178\n  来源词总得分: 0.22\n  系数: 0.22【计算公式】base_score × 系数 = 0.23 × 0.22\n【最终得分(截断后)】0.05",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "行为"
+        ],
+        [
+          "猫咪",
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.09,
+        0.15
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映行为猫咪梗图_r2_90": {
+      "type": "domain_combination",
+      "query": "反映行为猫咪梗图",
+      "level": 22,
+      "relevance_score": 0.1881792,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映行为猫咪梗图\"\n【评估对象】词条\"反映行为猫咪梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.90】原始问题和词条的动机都是寻找/查看梗图,动机一致。\n【品类维度 0.78】核心主体“猫咪梗图”匹配,限定词“反映行为”匹配,但缺失了“人类双标”这一重要限定词,导致匹配度有所下降。\n【最终得分 0.86】\n【规则说明】规则A:动机高分保护生效(动机0.90≥0.8),实际得分0.86已≥0.7\n【加权系数计算】\n0.2178\n  来源词总得分: 0.22\n  系数: 0.22【计算公式】base_score × 系数 = 0.86 × 0.22\n【最终得分(截断后)】0.19",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "行为"
+        ],
+        [
+          "猫咪",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.09,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.09,
+      "scoreColor": "#22c55e"
+    },
+    "comb_反映行为表情包梗图_r2_91": {
+      "type": "domain_combination",
+      "query": "反映行为表情包梗图",
+      "level": 22,
+      "relevance_score": 0.153468,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映行为表情包梗图\"\n【评估对象】词条\"反映行为表情包梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/获取反映人类双标行为的猫咪表情包梗图\n【动机维度 0.90】原始问题和词条的核心动机都是'寻找/获取'表情包梗图,词条是原始问题动机的泛化,动机一致。\n【品类维度 0.70】核心主体“表情包梗图”匹配,限定词“反映行为”与“反映人类双标行为”部分匹配,但缺失了“人类双标”和“猫咪”这两个重要限定词,属于核心主体匹配但限定词缺失的情况。\n【最终得分 0.84】\n【规则说明】规则A:动机高分保护生效(动机0.90≥0.8),实际得分0.84已≥0.7\n【加权系数计算】\n0.1827\n  来源词总得分: 0.18\n  系数: 0.18【计算公式】base_score × 系数 = 0.84 × 0.18\n【最终得分(截断后)】0.15",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "行为"
+        ],
+        [
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.15,
+      "scoreColor": "#22c55e"
+    },
+    "comb_反映行为猫咪表情包梗图_r2_92": {
+      "type": "domain_combination",
+      "query": "反映行为猫咪表情包梗图",
+      "level": 22,
+      "relevance_score": 0.0564408,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映行为猫咪表情包梗图\"\n【评估对象】词条\"反映行为猫咪表情包梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条都无明确的动作意图,无法评估动作匹配度。\n【品类维度 0.78】核心主体“猫咪表情包梗图”匹配,限定词“反映行为”匹配,但缺失了“人类双标”这一关键限定词,属于核心主体匹配,存在限定词匹配的情况。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.2412\n  来源词总得分: 0.24\n  系数: 0.24【计算公式】base_score × 系数 = 0.23 × 0.24\n【最终得分(截断后)】0.06",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "行为"
+        ],
+        [
+          "猫咪",
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.09,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_人类双标猫咪_r2_93": {
+      "type": "domain_combination",
+      "query": "人类双标猫咪",
+      "level": 22,
+      "relevance_score": 0.040013999999999994,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"人类双标猫咪\"\n【评估对象】词条\"人类双标猫咪\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】原始问题无明确动作意图,主要描述一个主题或内容\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.78】核心主体“人类双标”和“猫咪”匹配,但缺少限定词“表情包梗图”,属于核心主体匹配,存在限定词匹配的情况。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.17099999999999999\n  来源词总得分: 0.17\n  系数: 0.17【计算公式】base_score × 系数 = 0.23 × 0.17\n【最终得分(截断后)】0.04",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "人类",
+          "双标"
+        ],
+        [
+          "猫咪"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.09
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.09,
+      "scoreColor": "#ef4444"
+    },
+    "comb_人类双标表情包_r2_94": {
+      "type": "domain_combination",
+      "query": "人类双标表情包",
+      "level": 22,
+      "relevance_score": 0.05405399999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"人类双标表情包\"\n【评估对象】词条\"人类双标表情包\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/获取\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.78】核心主体'人类双标'和限定词'表情包'匹配,但缺失了'猫咪'和'梗图'这两个限定词,属于核心主体匹配,存在限定词匹配的情况。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.23099999999999998\n  来源词总得分: 0.23\n  系数: 0.23【计算公式】base_score × 系数 = 0.23 × 0.23\n【最终得分(截断后)】0.05",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "人类",
+          "双标"
+        ],
+        [
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.15
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_人类双标梗图_r2_95": {
+      "type": "domain_combination",
+      "query": "人类双标梗图",
+      "level": 22,
+      "relevance_score": 0.02457,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"人类双标梗图\"\n【评估对象】词条\"人类双标梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】原始问题无明确动作意图,主要描述一个主题\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机维度匹配度。\n【品类维度 0.78】核心主体'人类双标梗图'与原始问题中的'人类双标行为的猫咪表情包梗图'匹配,但限定词'猫咪表情包'缺失,属于核心主体匹配,存在限定词匹配的情况。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.10500000000000001\n  来源词总得分: 0.11\n  系数: 0.11【计算公式】base_score × 系数 = 0.23 × 0.11\n【最终得分(截断后)】0.02",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "人类",
+          "双标"
+        ],
+        [
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.024,
+      "scoreColor": "#22c55e"
+    },
+    "comb_人类双标猫咪表情包_r2_96": {
+      "type": "domain_combination",
+      "query": "人类双标猫咪表情包",
+      "level": 22,
+      "relevance_score": 0.078732,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"人类双标猫咪表情包\"\n【评估对象】词条\"人类双标猫咪表情包\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】原始问题无明确动作意图,主要描述一个主题或内容\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机维度匹配度。\n【品类维度 0.90】核心主体“人类双标猫咪表情包”完全匹配,限定词“梗图”缺失,但“表情包”本身就包含“梗图”的含义,因此匹配度很高。\n【最终得分 0.27】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.27已≤0.5\n【加权系数计算】\n0.29159999999999997\n  来源词总得分: 0.29\n  系数: 0.29【计算公式】base_score × 系数 = 0.27 × 0.29\n【最终得分(截断后)】0.08",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "人类",
+          "双标"
+        ],
+        [
+          "猫咪",
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.09,
+        0.15
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_人类双标猫咪梗图_r2_97": {
+      "type": "domain_combination",
+      "query": "人类双标猫咪梗图",
+      "level": 22,
+      "relevance_score": 0.078732,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"人类双标猫咪梗图\"\n【评估对象】词条\"人类双标猫咪梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】原始问题无明确动作意图,主要描述一个主题或内容\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法进行动机维度匹配评估。\n【品类维度 0.90】核心主体“人类双标猫咪梗图”与原始问题“反映人类双标行为的猫咪表情包梗图”高度匹配,仅缺少“表情包”这一限定词,但语义上“梗图”通常包含“表情包”的含义,因此匹配度很高。\n【最终得分 0.27】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.27已≤0.5\n【加权系数计算】\n0.29159999999999997\n  来源词总得分: 0.29\n  系数: 0.29【计算公式】base_score × 系数 = 0.27 × 0.29\n【最终得分(截断后)】0.08",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "人类",
+          "双标"
+        ],
+        [
+          "猫咪",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.09,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.09,
+      "scoreColor": "#ef4444"
+    },
+    "comb_人类双标表情包梗图_r2_98": {
+      "type": "domain_combination",
+      "query": "人类双标表情包梗图",
+      "level": 22,
+      "relevance_score": 0.060021,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"人类双标表情包梗图\"\n【评估对象】词条\"人类双标表情包梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.78】核心主体“人类双标表情包梗图”与“反映人类双标行为的猫咪表情包梗图”匹配,但缺少了限定词“猫咪”,因此得分在0.75-0.95之间。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.2565\n  来源词总得分: 0.26\n  系数: 0.26【计算公式】base_score × 系数 = 0.23 × 0.26\n【最终得分(截断后)】0.06",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "人类",
+          "双标"
+        ],
+        [
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_人类双标猫咪表情包梗图_r2_99": {
+      "type": "domain_combination",
+      "query": "人类双标猫咪表情包梗图",
+      "level": 22,
+      "relevance_score": 0.09261,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"人类双标猫咪表情包梗图\"\n【评估对象】词条\"人类双标猫咪表情包梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】原始问题无明确动作意图,主要为描述性词语\n【动机维度 0.00】原始问题和词条都无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.98】核心主体“人类双标猫咪表情包梗图”完全匹配,限定词“反映”在词条中被省略,但语义上是高度一致的,因此匹配度极高。\n【最终得分 0.29】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.29已≤0.5\n【加权系数计算】\n0.315\n  来源词总得分: 0.32\n  系数: 0.32【计算公式】base_score × 系数 = 0.29 × 0.32\n【最终得分(截断后)】0.09",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "人类",
+          "双标"
+        ],
+        [
+          "猫咪",
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.09,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_人类行为猫咪_r2_100": {
+      "type": "domain_combination",
+      "query": "人类行为猫咪",
+      "level": 22,
+      "relevance_score": 0.014175,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"人类行为猫咪\"\n【评估对象】词条\"人类行为猫咪\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】原始问题没有明确的动作意图,主要描述的是一种内容类型(表情包梗图)及其主题(反映人类双标行为的猫咪)。\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法进行动机维度匹配评估。\n【品类维度 0.35】核心主体词“人类”、“猫咪”匹配,但限定词“双标行为”、“表情包梗图”缺失,且“行为”在原词条中是限定词,在sug词中是主体词,存在语义错位。\n【最终得分 0.10】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.10已≤0.5\n【加权系数计算】\n0.135\n  来源词总得分: 0.14\n  系数: 0.14【计算公式】base_score × 系数 = 0.10 × 0.14\n【最终得分(截断后)】0.01",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "人类",
+          "行为"
+        ],
+        [
+          "猫咪"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.09
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.09,
+      "scoreColor": "#ef4444"
+    },
+    "comb_人类行为表情包_r2_101": {
+      "type": "domain_combination",
+      "query": "人类行为表情包",
+      "level": 22,
+      "relevance_score": 0.020475,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"人类行为表情包\"\n【评估对象】词条\"人类行为表情包\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.35】核心主体'表情包'匹配,'人类行为'是限定词的泛化,但缺失了'猫咪'和'双标行为'等关键限定词,导致匹配度中等偏低。\n【最终得分 0.10】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.10已≤0.5\n【加权系数计算】\n0.195\n  来源词总得分: 0.20\n  系数: 0.20【计算公式】base_score × 系数 = 0.10 × 0.20\n【最终得分(截断后)】0.02",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "人类",
+          "行为"
+        ],
+        [
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.15
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_人类行为梗图_r2_102": {
+      "type": "domain_combination",
+      "query": "人类行为梗图",
+      "level": 22,
+      "relevance_score": 0.007245000000000001,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"人类行为梗图\"\n【评估对象】词条\"人类行为梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.35】核心主体词'人类行为'匹配,但限定词'双标'、'猫咪表情包'、'梗图'缺失,且'梗图'是泛化概念,因此得分较低。\n【最终得分 0.10】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.10已≤0.5\n【加权系数计算】\n0.069\n  来源词总得分: 0.07\n  系数: 0.07【计算公式】base_score × 系数 = 0.10 × 0.07\n【最终得分(截断后)】0.01",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "人类",
+          "行为"
+        ],
+        [
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.024,
+      "scoreColor": "#ef4444"
+    },
+    "comb_人类行为猫咪表情包_r2_103": {
+      "type": "domain_combination",
+      "query": "人类行为猫咪表情包",
+      "level": 22,
+      "relevance_score": 0.05981039999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"人类行为猫咪表情包\"\n【评估对象】词条\"人类行为猫咪表情包\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.78】核心主体“猫咪表情包”匹配,限定词“人类行为”匹配,但缺失了“双标行为”和“梗图”这两个限定词,属于核心主体匹配,存在限定词匹配的情况。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.2556\n  来源词总得分: 0.26\n  系数: 0.26【计算公式】base_score × 系数 = 0.23 × 0.26\n【最终得分(截断后)】0.06",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "人类",
+          "行为"
+        ],
+        [
+          "猫咪",
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.09,
+        0.15
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_人类行为猫咪梗图_r2_104": {
+      "type": "domain_combination",
+      "query": "人类行为猫咪梗图",
+      "level": 22,
+      "relevance_score": 0.05981039999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"人类行为猫咪梗图\"\n【评估对象】词条\"人类行为猫咪梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】原始问题无明确动作意图,主要描述一个主题\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.78】核心主体“猫咪梗图”匹配,限定词“人类行为”匹配,但缺失了“双标行为”这一更具体的限定词,因此得分较高但未满分。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.2556\n  来源词总得分: 0.26\n  系数: 0.26【计算公式】base_score × 系数 = 0.23 × 0.26\n【最终得分(截断后)】0.06",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "人类",
+          "行为"
+        ],
+        [
+          "猫咪",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.09,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.09,
+      "scoreColor": "#ef4444"
+    },
+    "comb_人类行为表情包梗图_r2_105": {
+      "type": "domain_combination",
+      "query": "人类行为表情包梗图",
+      "level": 22,
+      "relevance_score": 0.04961249999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"人类行为表情包梗图\"\n【评估对象】词条\"人类行为表情包梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.75】核心主体“表情包梗图”匹配,限定词“人类行为”匹配,但缺失了“双标行为”和“猫咪”这两个限定词,属于核心主体匹配,存在限定词匹配但有缺失的情况。\n【最终得分 0.22】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.22已≤0.5\n【加权系数计算】\n0.22049999999999997\n  来源词总得分: 0.22\n  系数: 0.22【计算公式】base_score × 系数 = 0.22 × 0.22\n【最终得分(截断后)】0.05",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "人类",
+          "行为"
+        ],
+        [
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_人类行为猫咪表情包梗图_r2_106": {
+      "type": "domain_combination",
+      "query": "人类行为猫咪表情包梗图",
+      "level": 22,
+      "relevance_score": 0.071145,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"人类行为猫咪表情包梗图\"\n【评估对象】词条\"人类行为猫咪表情包梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.85】核心主体“猫咪表情包梗图”完全匹配,限定词“人类行为”匹配,但缺少了更具体的限定词“双标行为”,属于核心主体匹配且存在限定词匹配的情况。\n【最终得分 0.26】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.26已≤0.5\n【加权系数计算】\n0.27899999999999997\n  来源词总得分: 0.28\n  系数: 0.28【计算公式】base_score × 系数 = 0.26 × 0.28\n【最终得分(截断后)】0.07",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "人类",
+          "行为"
+        ],
+        [
+          "猫咪",
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.09,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_双标行为猫咪_r2_107": {
+      "type": "domain_combination",
+      "query": "双标行为猫咪",
+      "level": 22,
+      "relevance_score": 0.038960999999999996,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"双标行为猫咪\"\n【评估对象】词条\"双标行为猫咪\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】原始问题无明确动作意图,主要描述一个主题\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.78】核心主体'双标行为猫咪'与原始问题中的'双标行为的猫咪'高度匹配,但原始问题中包含'表情包梗图'这一限定词,词条中未体现,因此略有缺失。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.16649999999999998\n  来源词总得分: 0.17\n  系数: 0.17【计算公式】base_score × 系数 = 0.23 × 0.17\n【最终得分(截断后)】0.04",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "双标",
+          "行为"
+        ],
+        [
+          "猫咪"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.09
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.09,
+      "scoreColor": "#ef4444"
+    },
+    "comb_双标行为表情包_r2_108": {
+      "type": "domain_combination",
+      "query": "双标行为表情包",
+      "level": 22,
+      "relevance_score": 0.05096249999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"双标行为表情包\"\n【评估对象】词条\"双标行为表情包\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】原始问题无明确动作意图,主要描述一个主题\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.75】核心主体'双标行为'和限定词'表情包'匹配,但缺失了'猫咪'和'梗图'这两个限定词,属于核心主体匹配,存在限定词匹配的情况。\n【最终得分 0.22】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.22已≤0.5\n【加权系数计算】\n0.22649999999999998\n  来源词总得分: 0.23\n  系数: 0.23【计算公式】base_score × 系数 = 0.22 × 0.23\n【最终得分(截断后)】0.05",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "双标",
+          "行为"
+        ],
+        [
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.15
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_双标行为梗图_r2_109": {
+      "type": "domain_combination",
+      "query": "双标行为梗图",
+      "level": 22,
+      "relevance_score": 0.023517,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"双标行为梗图\"\n【评估对象】词条\"双标行为梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条都无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.78】核心主体“双标行为梗图”完全匹配,但缺失了限定词“猫咪表情包”,因此得分在0.75-0.95区间。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.1005\n  来源词总得分: 0.10\n  系数: 0.10【计算公式】base_score × 系数 = 0.23 × 0.10\n【最终得分(截断后)】0.02",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "双标",
+          "行为"
+        ],
+        [
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.024,
+      "scoreColor": "#ef4444"
+    },
+    "comb_双标行为猫咪表情包_r2_110": {
+      "type": "domain_combination",
+      "query": "双标行为猫咪表情包",
+      "level": 22,
+      "relevance_score": 0.077517,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"双标行为猫咪表情包\"\n【评估对象】词条\"双标行为猫咪表情包\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/获取\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.90】核心主体“双标行为猫咪表情包”完全匹配,但缺少了“梗图”这一限定词,因此略低于满分。\n【最终得分 0.27】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.27已≤0.5\n【加权系数计算】\n0.28709999999999997\n  来源词总得分: 0.29\n  系数: 0.29【计算公式】base_score × 系数 = 0.27 × 0.29\n【最终得分(截断后)】0.08",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "双标",
+          "行为"
+        ],
+        [
+          "猫咪",
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.09,
+        0.15
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_双标行为猫咪梗图_r2_111": {
+      "type": "domain_combination",
+      "query": "双标行为猫咪梗图",
+      "level": 22,
+      "relevance_score": 0.077517,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"双标行为猫咪梗图\"\n【评估对象】词条\"双标行为猫咪梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】原始问题无明确动作意图,主要描述一个主题或内容。\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法进行动机维度匹配评估。\n【品类维度 0.90】核心主体“双标行为猫咪梗图”与“反映人类双标行为的猫咪表情包梗图”高度匹配,限定词“表情包”在词条中省略但语义上一致,因此匹配度高。\n【最终得分 0.27】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.27已≤0.5\n【加权系数计算】\n0.28709999999999997\n  来源词总得分: 0.29\n  系数: 0.29【计算公式】base_score × 系数 = 0.27 × 0.29\n【最终得分(截断后)】0.08",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "双标",
+          "行为"
+        ],
+        [
+          "猫咪",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.09,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.09,
+      "scoreColor": "#ef4444"
+    },
+    "comb_双标行为表情包梗图_r2_112": {
+      "type": "domain_combination",
+      "query": "双标行为表情包梗图",
+      "level": 22,
+      "relevance_score": 0.058968,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"双标行为表情包梗图\"\n【评估对象】词条\"双标行为表情包梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.78】核心主体“双标行为表情包梗图”完全匹配,但缺失了限定词“猫咪”,导致匹配度略有下降。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.252\n  来源词总得分: 0.25\n  系数: 0.25【计算公式】base_score × 系数 = 0.23 × 0.25\n【最终得分(截断后)】0.06",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "双标",
+          "行为"
+        ],
+        [
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_双标行为猫咪表情包梗图_r2_113": {
+      "type": "domain_combination",
+      "query": "双标行为猫咪表情包梗图",
+      "level": 22,
+      "relevance_score": 0.091287,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"双标行为猫咪表情包梗图\"\n【评估对象】词条\"双标行为猫咪表情包梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】无明确动作意图\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.98】核心主体“双标行为猫咪表情包梗图”完全匹配,限定词“反映人类”在词条中被省略,但语义上是高度一致的,属于合理省略。\n【最终得分 0.29】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.29已≤0.5\n【加权系数计算】\n0.3105\n  来源词总得分: 0.31\n  系数: 0.31【计算公式】base_score × 系数 = 0.29 × 0.31\n【最终得分(截断后)】0.09",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "双标",
+          "行为"
+        ],
+        [
+          "猫咪",
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.09,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映人类双标猫咪_r2_114": {
+      "type": "domain_combination",
+      "query": "反映人类双标猫咪",
+      "level": 22,
+      "relevance_score": 0.041698799999999994,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映人类双标猫咪\"\n【评估对象】词条\"反映人类双标猫咪\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/获取\n【动机维度 0.00】词条无明确动作意图,无法评估动作匹配度\n【品类维度 0.78】核心主体“人类双标猫咪”匹配,但缺少了限定词“表情包梗图”,导致匹配度略有下降。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.1782\n  来源词总得分: 0.18\n  系数: 0.18【计算公式】base_score × 系数 = 0.23 × 0.18\n【最终得分(截断后)】0.04",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "人类",
+          "双标"
+        ],
+        [
+          "猫咪"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.09
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.09,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映人类双标表情包_r2_115": {
+      "type": "domain_combination",
+      "query": "反映人类双标表情包",
+      "level": 22,
+      "relevance_score": 0.053595,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映人类双标表情包\"\n【评估对象】词条\"反映人类双标表情包\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】无明确动作意图,主要为描述性内容\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法进行动机维度评估。\n【品类维度 0.75】核心主体“反映人类双标表情包”匹配,但缺失了限定词“猫咪”和“梗图”。\n【最终得分 0.22】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.22已≤0.5\n【加权系数计算】\n0.2382\n  来源词总得分: 0.24\n  系数: 0.24【计算公式】base_score × 系数 = 0.22 × 0.24\n【最终得分(截断后)】0.05",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "人类",
+          "双标"
+        ],
+        [
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.15
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映人类双标梗图_r2_116": {
+      "type": "domain_combination",
+      "query": "反映人类双标梗图",
+      "level": 22,
+      "relevance_score": 0.0262548,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映人类双标梗图\"\n【评估对象】词条\"反映人类双标梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.78】核心主体“人类双标梗图”完全匹配,但限定词“猫咪表情包”缺失,属于核心主体匹配但限定词部分缺失的情况。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.1122\n  来源词总得分: 0.11\n  系数: 0.11【计算公式】base_score × 系数 = 0.23 × 0.11\n【最终得分(截断后)】0.03",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "人类",
+          "双标"
+        ],
+        [
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.024,
+      "scoreColor": "#22c55e"
+    },
+    "comb_反映人类双标猫咪表情包_r2_117": {
+      "type": "domain_combination",
+      "query": "反映人类双标猫咪表情包",
+      "level": 22,
+      "relevance_score": 0.28565279999999993,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映人类双标猫咪表情包\"\n【评估对象】词条\"反映人类双标猫咪表情包\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.98】原始问题和词条都隐含了'寻找/查看'的动机,词条是原始问题动机的同义表达,动机完全一致。\n【品类维度 0.90】核心主体“人类双标行为的猫咪表情包”与“人类双标猫咪表情包”高度匹配,限定词“梗图”在词条中缺失,但语义上高度相关,因此评分较高。\n【最终得分 0.96】\n【规则说明】规则A:动机高分保护生效(动机0.98≥0.8),实际得分0.96已≥0.7\n【加权系数计算】\n0.29879999999999995\n  来源词总得分: 0.30\n  系数: 0.30【计算公式】base_score × 系数 = 0.96 × 0.30\n【最终得分(截断后)】0.29",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "人类",
+          "双标"
+        ],
+        [
+          "猫咪",
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.09,
+        0.15
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.15,
+      "scoreColor": "#22c55e"
+    },
+    "comb_反映人类双标猫咪梗图_r2_118": {
+      "type": "domain_combination",
+      "query": "反映人类双标猫咪梗图",
+      "level": 22,
+      "relevance_score": 0.080676,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映人类双标猫咪梗图\"\n【评估对象】词条\"反映人类双标猫咪梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/了解\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.90】核心主体“人类双标猫咪梗图”完全匹配,限定词“反映”匹配,但缺少“表情包”这一限定词,因此略有扣分。\n【最终得分 0.27】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.27已≤0.5\n【加权系数计算】\n0.29879999999999995\n  来源词总得分: 0.30\n  系数: 0.30【计算公式】base_score × 系数 = 0.27 × 0.30\n【最终得分(截断后)】0.08",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "人类",
+          "双标"
+        ],
+        [
+          "猫咪",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.09,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.09,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映人类双标表情包梗图_r2_119": {
+      "type": "domain_combination",
+      "query": "反映人类双标表情包梗图",
+      "level": 22,
+      "relevance_score": 0.06170579999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映人类双标表情包梗图\"\n【评估对象】词条\"反映人类双标表情包梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确动作意图,无法评估动作匹配度。\n【品类维度 0.78】核心主体“反映人类双标表情包梗图”完全匹配,但限定词“猫咪”缺失,属于核心主体匹配但限定词部分缺失的情况。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.2637\n  来源词总得分: 0.26\n  系数: 0.26【计算公式】base_score × 系数 = 0.23 × 0.26\n【最终得分(截断后)】0.06",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "人类",
+          "双标"
+        ],
+        [
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映人类双标猫咪表情包梗图_r2_120": {
+      "type": "domain_combination",
+      "query": "反映人类双标猫咪表情包梗图",
+      "level": 22,
+      "relevance_score": 0.09472679999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映人类双标猫咪表情包梗图\"\n【评估对象】词条\"反映人类双标猫咪表情包梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.98】核心主体和所有关键限定词(人类双标行为、猫咪表情包梗图)都完全匹配,仅在限定词'行为'上存在细微差异,但语义上高度一致。\n【最终得分 0.29】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.29已≤0.5\n【加权系数计算】\n0.3222\n  来源词总得分: 0.32\n  系数: 0.32【计算公式】base_score × 系数 = 0.29 × 0.32\n【最终得分(截断后)】0.09",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "人类",
+          "双标"
+        ],
+        [
+          "猫咪",
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.09,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映人类行为猫咪_r2_121": {
+      "type": "domain_combination",
+      "query": "反映人类行为猫咪",
+      "level": 22,
+      "relevance_score": 0.0374868,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映人类行为猫咪\"\n【评估对象】词条\"反映人类行为猫咪\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】无明确动作意图\n【动机维度 0.00】原始问题和词条均无明确动作意图,无法评估动作匹配度。\n【品类维度 0.78】核心主体'人类行为猫咪'与原始问题'人类双标行为的猫咪表情包梗图'中的'人类双标行为的猫咪'部分高度匹配,但缺少了'双标'和'表情包梗图'这两个限定词,因此给予较高但非满分。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.1602\n  来源词总得分: 0.16\n  系数: 0.16【计算公式】base_score × 系数 = 0.23 × 0.16\n【最终得分(截断后)】0.04",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "人类",
+          "行为"
+        ],
+        [
+          "猫咪"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.09
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.09,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映人类行为表情包_r2_122": {
+      "type": "domain_combination",
+      "query": "反映人类行为表情包",
+      "level": 22,
+      "relevance_score": 0.0515268,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映人类行为表情包\"\n【评估对象】词条\"反映人类行为表情包\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】原始问题无明确动作意图,主要描述一个主题或内容\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动作匹配度。\n【品类维度 0.78】核心主体“表情包”和“人类行为”匹配,但限定词“双标行为”、“猫咪”、“梗图”未匹配,属于核心主体匹配,存在限定词匹配的情况。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.2202\n  来源词总得分: 0.22\n  系数: 0.22【计算公式】base_score × 系数 = 0.23 × 0.22\n【最终得分(截断后)】0.05",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "人类",
+          "行为"
+        ],
+        [
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.15
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映人类行为梗图_r2_123": {
+      "type": "domain_combination",
+      "query": "反映人类行为梗图",
+      "level": 22,
+      "relevance_score": 0.01413,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映人类行为梗图\"\n【评估对象】词条\"反映人类行为梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.50】核心主体'人类行为梗图'匹配,但限定词'双标'和'猫咪表情包'缺失,导致匹配度中等。\n【最终得分 0.15】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.15已≤0.5\n【加权系数计算】\n0.0942\n  来源词总得分: 0.09\n  系数: 0.09【计算公式】base_score × 系数 = 0.15 × 0.09\n【最终得分(截断后)】0.01",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "人类",
+          "行为"
+        ],
+        [
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.024,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映人类行为猫咪表情包_r2_124": {
+      "type": "domain_combination",
+      "query": "反映人类行为猫咪表情包",
+      "level": 22,
+      "relevance_score": 0.071604,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映人类行为猫咪表情包\"\n【评估对象】词条\"反映人类行为猫咪表情包\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.85】核心主体“人类行为猫咪表情包”完全匹配,限定词“双标”缺失,但整体品类高度相关。\n【最终得分 0.26】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.26已≤0.5\n【加权系数计算】\n0.2808\n  来源词总得分: 0.28\n  系数: 0.28【计算公式】base_score × 系数 = 0.26 × 0.28\n【最终得分(截断后)】0.07",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "人类",
+          "行为"
+        ],
+        [
+          "猫咪",
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.09,
+        0.15
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映人类行为猫咪梗图_r2_125": {
+      "type": "domain_combination",
+      "query": "反映人类行为猫咪梗图",
+      "level": 22,
+      "relevance_score": 0.071604,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映人类行为猫咪梗图\"\n【评估对象】词条\"反映人类行为猫咪梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.85】核心主体“人类行为猫咪梗图”完全匹配,但缺失了限定词“双标”\n【最终得分 0.26】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.26已≤0.5\n【加权系数计算】\n0.2808\n  来源词总得分: 0.28\n  系数: 0.28【计算公式】base_score × 系数 = 0.26 × 0.28\n【最终得分(截断后)】0.07",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "人类",
+          "行为"
+        ],
+        [
+          "猫咪",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.09,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.09,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映人类行为表情包梗图_r2_126": {
+      "type": "domain_combination",
+      "query": "反映人类行为表情包梗图",
+      "level": 22,
+      "relevance_score": 0.05749379999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映人类行为表情包梗图\"\n【评估对象】词条\"反映人类行为表情包梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.78】核心主体“表情包梗图”匹配,限定词“人类行为”匹配,但缺失了“双标”和“猫咪”这两个限定词,属于核心主体匹配,存在限定词匹配的情况。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.24569999999999997\n  来源词总得分: 0.25\n  系数: 0.25【计算公式】base_score × 系数 = 0.23 × 0.25\n【最终得分(截断后)】0.06",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "人类",
+          "行为"
+        ],
+        [
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映人类行为猫咪表情包梗图_r2_127": {
+      "type": "domain_combination",
+      "query": "反映人类行为猫咪表情包梗图",
+      "level": 22,
+      "relevance_score": 0.07118279999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映人类行为猫咪表情包梗图\"\n【评估对象】词条\"反映人类行为猫咪表情包梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确动作意图,无法评估动作匹配度。\n【品类维度 0.78】核心主体“猫咪表情包梗图”和限定词“反映人类行为”完全匹配,但缺失了“双标”这一关键限定词,属于核心主体匹配,存在限定词匹配的情况。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.30419999999999997\n  来源词总得分: 0.30\n  系数: 0.30【计算公式】base_score × 系数 = 0.23 × 0.30\n【最终得分(截断后)】0.07",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "人类",
+          "行为"
+        ],
+        [
+          "猫咪",
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.09,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映双标行为猫咪_r2_128": {
+      "type": "domain_combination",
+      "query": "反映双标行为猫咪",
+      "level": 22,
+      "relevance_score": 0.042457499999999995,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映双标行为猫咪\"\n【评估对象】词条\"反映双标行为猫咪\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.85】核心主体“双标行为猫咪”完全匹配,但缺失了“表情包梗图”这一限定词,属于核心主体匹配,存在限定词匹配的情况。\n【最终得分 0.26】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.26已≤0.5\n【加权系数计算】\n0.16649999999999998\n  来源词总得分: 0.17\n  系数: 0.17【计算公式】base_score × 系数 = 0.26 × 0.17\n【最终得分(截断后)】0.04",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "双标",
+          "行为"
+        ],
+        [
+          "猫咪"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.09
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.09,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映双标行为表情包_r2_129": {
+      "type": "domain_combination",
+      "query": "反映双标行为表情包",
+      "level": 22,
+      "relevance_score": 0.05300099999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映双标行为表情包\"\n【评估对象】词条\"反映双标行为表情包\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/获取\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.78】核心主体“双标行为表情包”匹配,但缺失了“人类”和“猫咪”这两个重要的限定词,因此评分在0.75-0.95之间。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.22649999999999998\n  来源词总得分: 0.23\n  系数: 0.23【计算公式】base_score × 系数 = 0.23 × 0.23\n【最终得分(截断后)】0.05",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "双标",
+          "行为"
+        ],
+        [
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.15
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映双标行为梗图_r2_130": {
+      "type": "domain_combination",
+      "query": "反映双标行为梗图",
+      "level": 22,
+      "relevance_score": 0.0226125,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映双标行为梗图\"\n【评估对象】词条\"反映双标行为梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】原始问题无明确动作意图,主要描述对象和属性\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.75】核心主体“双标行为梗图”完全匹配,但缺失了限定词“人类”和“猫咪表情包”。\n【最终得分 0.22】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.22已≤0.5\n【加权系数计算】\n0.1005\n  来源词总得分: 0.10\n  系数: 0.10【计算公式】base_score × 系数 = 0.22 × 0.10\n【最终得分(截断后)】0.02",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "双标",
+          "行为"
+        ],
+        [
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.024,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映双标行为猫咪表情包_r2_131": {
+      "type": "domain_combination",
+      "query": "反映双标行为猫咪表情包",
+      "level": 22,
+      "relevance_score": 0.077517,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映双标行为猫咪表情包\"\n【评估对象】词条\"反映双标行为猫咪表情包\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.90】核心主体“双标行为猫咪表情包”完全匹配,限定词“梗图”缺失,但语义上“表情包”和“梗图”高度相关,属于合理泛化。\n【最终得分 0.27】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.27已≤0.5\n【加权系数计算】\n0.28709999999999997\n  来源词总得分: 0.29\n  系数: 0.29【计算公式】base_score × 系数 = 0.27 × 0.29\n【最终得分(截断后)】0.08",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "双标",
+          "行为"
+        ],
+        [
+          "猫咪",
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.09,
+        0.15
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映双标行为猫咪梗图_r2_132": {
+      "type": "domain_combination",
+      "query": "反映双标行为猫咪梗图",
+      "level": 22,
+      "relevance_score": 0.077517,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映双标行为猫咪梗图\"\n【评估对象】词条\"反映双标行为猫咪梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.90】核心主体“双标行为猫咪梗图”完全匹配,限定词“反映”也匹配,仅缺少“表情包”这一限定词,但“梗图”通常包含“表情包”的含义,因此匹配度很高。\n【最终得分 0.27】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.27已≤0.5\n【加权系数计算】\n0.28709999999999997\n  来源词总得分: 0.29\n  系数: 0.29【计算公式】base_score × 系数 = 0.27 × 0.29\n【最终得分(截断后)】0.08",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "双标",
+          "行为"
+        ],
+        [
+          "猫咪",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.09,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.09,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映双标行为表情包梗图_r2_133": {
+      "type": "domain_combination",
+      "query": "反映双标行为表情包梗图",
+      "level": 22,
+      "relevance_score": 0.217728,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映双标行为表情包梗图\"\n【评估对象】词条\"反映双标行为表情包梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.90】原始问题和词条的动作意图都是寻找/查看表情包梗图,动作意图高度一致。\n【品类维度 0.78】核心主体“双标行为表情包梗图”完全匹配,但限定词“猫咪”缺失,属于核心主体匹配但限定词部分缺失的情况。\n【最终得分 0.86】\n【规则说明】规则A:动机高分保护生效(动机0.90≥0.8),实际得分0.86已≥0.7\n【加权系数计算】\n0.252\n  来源词总得分: 0.25\n  系数: 0.25【计算公式】base_score × 系数 = 0.86 × 0.25\n【最终得分(截断后)】0.22",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "双标",
+          "行为"
+        ],
+        [
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.15,
+      "scoreColor": "#22c55e"
+    },
+    "comb_反映双标行为猫咪表情包梗图_r2_134": {
+      "type": "domain_combination",
+      "query": "反映双标行为猫咪表情包梗图",
+      "level": 22,
+      "relevance_score": 0.091287,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映双标行为猫咪表情包梗图\"\n【评估对象】词条\"反映双标行为猫咪表情包梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】无明确动作意图\n【动机维度 0.00】原始问题和词条均无明确动作意图,无法评估动作匹配度。\n【品类维度 0.98】核心主体“双标行为猫咪表情包梗图”完全匹配,限定词“反映”也完全匹配,仅缺少一个重复的“的”字,匹配度极高。\n【最终得分 0.29】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.29已≤0.5\n【加权系数计算】\n0.3105\n  来源词总得分: 0.31\n  系数: 0.31【计算公式】base_score × 系数 = 0.29 × 0.31\n【最终得分(截断后)】0.09",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "双标",
+          "行为"
+        ],
+        [
+          "猫咪",
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.09,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_人类双标行为猫咪_r2_135": {
+      "type": "domain_combination",
+      "query": "人类双标行为猫咪",
+      "level": 22,
+      "relevance_score": 0.040013999999999994,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"人类双标行为猫咪\"\n【评估对象】词条\"人类双标行为猫咪\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/获取\n【动机维度 0.00】词条无明确动作意图,无法评估动作匹配度\n【品类维度 0.78】核心主体“人类双标行为”和“猫咪”完全匹配,但限定词“表情包梗图”缺失,属于核心主体匹配,存在限定词缺失的情况。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.17099999999999999\n  来源词总得分: 0.17\n  系数: 0.17【计算公式】base_score × 系数 = 0.23 × 0.17\n【最终得分(截断后)】0.04",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "人类",
+          "双标",
+          "行为"
+        ],
+        [
+          "猫咪"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.09
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.09,
+      "scoreColor": "#ef4444"
+    },
+    "comb_人类双标行为表情包_r2_136": {
+      "type": "domain_combination",
+      "query": "人类双标行为表情包",
+      "level": 22,
+      "relevance_score": 0.05405399999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"人类双标行为表情包\"\n【评估对象】词条\"人类双标行为表情包\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】原始问题无明确动作意图,主要描述一个主题\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.78】核心主体“人类双标行为”完全匹配,限定词“表情包”也匹配。但缺少了“猫咪”和“梗图”这两个限定词,因此未能达到满分。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.23099999999999998\n  来源词总得分: 0.23\n  系数: 0.23【计算公式】base_score × 系数 = 0.23 × 0.23\n【最终得分(截断后)】0.05",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "人类",
+          "双标",
+          "行为"
+        ],
+        [
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.15
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_人类双标行为梗图_r2_137": {
+      "type": "domain_combination",
+      "query": "人类双标行为梗图",
+      "level": 22,
+      "relevance_score": 0.02457,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"人类双标行为梗图\"\n【评估对象】词条\"人类双标行为梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】原始问题无明确动作意图,主要为“反映”或“寻找”\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.78】核心主体“人类双标行为梗图”完全匹配,但缺失了限定词“猫咪表情包”,因此给予较高但非满分评分。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.10500000000000001\n  来源词总得分: 0.11\n  系数: 0.11【计算公式】base_score × 系数 = 0.23 × 0.11\n【最终得分(截断后)】0.02",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "人类",
+          "双标",
+          "行为"
+        ],
+        [
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.024,
+      "scoreColor": "#22c55e"
+    },
+    "comb_人类双标行为猫咪表情包_r2_138": {
+      "type": "domain_combination",
+      "query": "人类双标行为猫咪表情包",
+      "level": 22,
+      "relevance_score": 0.08310599999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"人类双标行为猫咪表情包\"\n【评估对象】词条\"人类双标行为猫咪表情包\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/获取\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.95】核心主体“人类双标行为”、“猫咪表情包”完全匹配,限定词“梗图”在词条中被省略,但语义上高度一致,属于合理泛化。\n【最终得分 0.28】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.28已≤0.5\n【加权系数计算】\n0.29159999999999997\n  来源词总得分: 0.29\n  系数: 0.29【计算公式】base_score × 系数 = 0.28 × 0.29\n【最终得分(截断后)】0.08",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "人类",
+          "双标",
+          "行为"
+        ],
+        [
+          "猫咪",
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.09,
+        0.15
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_人类双标行为猫咪梗图_r2_139": {
+      "type": "domain_combination",
+      "query": "人类双标行为猫咪梗图",
+      "level": 22,
+      "relevance_score": 0.08573039999999998,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"人类双标行为猫咪梗图\"\n【评估对象】词条\"人类双标行为猫咪梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】无明确动作意图\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.98】核心主体“人类双标行为猫咪梗图”完全匹配,限定词“表情包”在词条中被省略,但语义上高度一致,属于可接受的微小差异。\n【最终得分 0.29】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.29已≤0.5\n【加权系数计算】\n0.29159999999999997\n  来源词总得分: 0.29\n  系数: 0.29【计算公式】base_score × 系数 = 0.29 × 0.29\n【最终得分(截断后)】0.09",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "人类",
+          "双标",
+          "行为"
+        ],
+        [
+          "猫咪",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.09,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.09,
+      "scoreColor": "#ef4444"
+    },
+    "comb_人类双标行为表情包梗图_r2_140": {
+      "type": "domain_combination",
+      "query": "人类双标行为表情包梗图",
+      "level": 22,
+      "relevance_score": 0.060021,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"人类双标行为表情包梗图\"\n【评估对象】词条\"人类双标行为表情包梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.78】核心主体“人类双标行为表情包梗图”完全匹配,但限定词“猫咪”缺失,导致匹配度略有下降。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.2565\n  来源词总得分: 0.26\n  系数: 0.26【计算公式】base_score × 系数 = 0.23 × 0.26\n【最终得分(截断后)】0.06",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "人类",
+          "双标",
+          "行为"
+        ],
+        [
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_人类双标行为猫咪表情包梗图_r2_141": {
+      "type": "domain_combination",
+      "query": "人类双标行为猫咪表情包梗图",
+      "level": 22,
+      "relevance_score": 0.0945,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"人类双标行为猫咪表情包梗图\"\n【评估对象】词条\"人类双标行为猫咪表情包梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】原始问题无明确动作意图,主要为描述性词语,意图是“了解/寻找”关于“反映人类双标行为的猫咪表情包梗图”\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机维度匹配度。\n【品类维度 1.00】核心主体“人类双标行为猫咪表情包梗图”与限定词“反映”完全匹配,语义一致,只是省略了动词,不影响品类匹配度。\n【最终得分 0.30】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.30已≤0.5\n【加权系数计算】\n0.315\n  来源词总得分: 0.32\n  系数: 0.32【计算公式】base_score × 系数 = 0.30 × 0.32\n【最终得分(截断后)】0.09",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "人类",
+          "双标",
+          "行为"
+        ],
+        [
+          "猫咪",
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.09,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映人类双标行为猫咪_r2_142": {
+      "type": "domain_combination",
+      "query": "反映人类双标行为猫咪",
+      "level": 22,
+      "relevance_score": 0.045441,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映人类双标行为猫咪\"\n【评估对象】词条\"反映人类双标行为猫咪\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/获取\n【动机维度 0.00】词条无明确动作意图,无法评估动作匹配度\n【品类维度 0.85】核心主体“人类双标行为猫咪”完全匹配,但缺少了“表情包梗图”这一限定词,属于核心主体匹配但限定词不完全匹配的情况。\n【最终得分 0.26】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.26已≤0.5\n【加权系数计算】\n0.1782\n  来源词总得分: 0.18\n  系数: 0.18【计算公式】base_score × 系数 = 0.26 × 0.18\n【最终得分(截断后)】0.05",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "人类",
+          "双标",
+          "行为"
+        ],
+        [
+          "猫咪"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.024,
+        0.09
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.09,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映人类双标行为表情包_r2_143": {
+      "type": "domain_combination",
+      "query": "反映人类双标行为表情包",
+      "level": 22,
+      "relevance_score": 0.0557388,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映人类双标行为表情包\"\n【评估对象】词条\"反映人类双标行为表情包\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/获取\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.78】核心主体“人类双标行为表情包”匹配,但缺失了限定词“猫咪”和“梗图”。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.2382\n  来源词总得分: 0.24\n  系数: 0.24【计算公式】base_score × 系数 = 0.23 × 0.24\n【最终得分(截断后)】0.06",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "人类",
+          "双标",
+          "行为"
+        ],
+        [
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.024,
+        0.15
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映人类双标行为梗图_r2_144": {
+      "type": "domain_combination",
+      "query": "反映人类双标行为梗图",
+      "level": 22,
+      "relevance_score": 0.0262548,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映人类双标行为梗图\"\n【评估对象】词条\"反映人类双标行为梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条均无明确动作意图,无法评估动作匹配度。\n【品类维度 0.78】核心主体'反映人类双标行为梗图'完全匹配,但缺失了限定词'猫咪表情包',属于核心主体匹配但限定词不完全匹配的情况。\n【最终得分 0.23】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.23已≤0.5\n【加权系数计算】\n0.1122\n  来源词总得分: 0.11\n  系数: 0.11【计算公式】base_score × 系数 = 0.23 × 0.11\n【最终得分(截断后)】0.03",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "人类",
+          "双标",
+          "行为"
+        ],
+        [
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.024,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.024,
+      "scoreColor": "#22c55e"
+    },
+    "comb_反映人类双标行为猫咪表情包_r2_145": {
+      "type": "domain_combination",
+      "query": "反映人类双标行为猫咪表情包",
+      "level": 22,
+      "relevance_score": 0.08784719999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映人类双标行为猫咪表情包\"\n【评估对象】词条\"反映人类双标行为猫咪表情包\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/了解\n【动机维度 0.00】原始问题和词条均无明确的动作意图,无法评估动机匹配度。\n【品类维度 0.98】核心主体“人类双标行为猫咪表情包”完全匹配,限定词“梗图”在词条中被省略,但语义上高度相关,属于近义词或包含关系,因此匹配度极高。\n【最终得分 0.29】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.29已≤0.5\n【加权系数计算】\n0.29879999999999995\n  来源词总得分: 0.30\n  系数: 0.30【计算公式】base_score × 系数 = 0.29 × 0.30\n【最终得分(截断后)】0.09",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "人类",
+          "双标",
+          "行为"
+        ],
+        [
+          "猫咪",
+          "表情包"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.024,
+        0.09,
+        0.15
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映人类双标行为猫咪梗图_r2_146": {
+      "type": "domain_combination",
+      "query": "反映人类双标行为猫咪梗图",
+      "level": 22,
+      "relevance_score": 0.08784719999999999,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映人类双标行为猫咪梗图\"\n【评估对象】词条\"反映人类双标行为猫咪梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】无明确动作意图\n【动机维度 0.00】原始问题无明确动作意图,无法评估动作匹配度\n【品类维度 0.98】核心主体“人类双标行为猫咪梗图”完全匹配,限定词“表情包”在词条中被省略,但语义上高度相关,属于合理泛化或省略,因此匹配度极高。\n【最终得分 0.29】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.29已≤0.5\n【加权系数计算】\n0.29879999999999995\n  来源词总得分: 0.30\n  系数: 0.30【计算公式】base_score × 系数 = 0.29 × 0.30\n【最终得分(截断后)】0.09",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "人类",
+          "双标",
+          "行为"
+        ],
+        [
+          "猫咪",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.024,
+        0.09,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.09,
+      "scoreColor": "#ef4444"
+    },
+    "comb_反映人类双标行为表情包梗图_r2_147": {
+      "type": "domain_combination",
+      "query": "反映人类双标行为表情包梗图",
+      "level": 22,
+      "relevance_score": 0.22783679999999998,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映人类双标行为表情包梗图\"\n【评估对象】词条\"反映人类双标行为表情包梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.90】原始问题和词条的动作意图都是“寻找/查看”表情包梗图,动作意图高度一致。\n【品类维度 0.78】核心主体'反映人类双标行为表情包梗图'完全匹配,但限定词'猫咪'缺失,属于核心主体匹配,存在限定词匹配的情况。\n【最终得分 0.86】\n【规则说明】规则A:动机高分保护生效(动机0.90≥0.8),实际得分0.86已≥0.7\n【加权系数计算】\n0.2637\n  来源词总得分: 0.26\n  系数: 0.26【计算公式】base_score × 系数 = 0.86 × 0.26\n【最终得分(截断后)】0.23",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "人类",
+          "双标",
+          "行为"
+        ],
+        [
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.024,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": true,
+      "max_source_score": 0.15,
+      "scoreColor": "#22c55e"
+    },
+    "comb_反映人类双标行为猫咪表情包梗图_r2_148": {
+      "type": "domain_combination",
+      "query": "反映人类双标行为猫咪表情包梗图",
+      "level": 22,
+      "relevance_score": 0.09666,
+      "evaluationReason": "【Round 2+ 域间评估】\n【评估对象】组合\"反映人类双标行为猫咪表情包梗图\"\n【评估对象】词条\"反映人类双标行为猫咪表情包梗图\" vs 作用域词条\"反映人类双标行为的猫咪表情包梗图\"\n【核心动机】寻找/查看\n【动机维度 0.00】原始问题和词条都无明确的动作意图,无法评估动作匹配度。\n【品类维度 1.00】核心主体和所有关键限定词完全匹配,仅缺少一个不影响语义的标点符号。\n【最终得分 0.30】\n【规则说明】规则B:动机低分限制生效(动机0.00≤0.2),实际得分0.30已≤0.5\n【加权系数计算】\n0.3222\n  来源词总得分: 0.32\n  系数: 0.32【计算公式】base_score × 系数 = 0.30 × 0.32\n【最终得分(截断后)】0.10",
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "source_words": [
+        [
+          "反映",
+          "人类",
+          "双标",
+          "行为"
+        ],
+        [
+          "猫咪",
+          "表情包",
+          "梗图"
+        ]
+      ],
+      "from_segments": [
+        "反映人类双标行为的",
+        "猫咪表情包梗图"
+      ],
+      "domains": [
+        2,
+        3
+      ],
+      "domains_str": "D2,D3",
+      "source_word_details": [
+        {
+          "domain_index": 2,
+          "segment_type": "修饰短语",
+          "segment_text": "反映人类双标行为的",
+          "words": [
+            {
+              "text": "反映",
+              "score": 0.024
+            },
+            {
+              "text": "人类",
+              "score": 0.024
+            },
+            {
+              "text": "双标",
+              "score": 0.024
+            },
+            {
+              "text": "行为",
+              "score": 0.024
+            }
+          ]
+        },
+        {
+          "domain_index": 3,
+          "segment_type": "中心名词",
+          "segment_text": "猫咪表情包梗图",
+          "words": [
+            {
+              "text": "猫咪",
+              "score": 0.09
+            },
+            {
+              "text": "表情包",
+              "score": 0.15
+            },
+            {
+              "text": "梗图",
+              "score": 0.024
+            }
+          ]
+        }
+      ],
+      "source_scores": [
+        0.024,
+        0.024,
+        0.024,
+        0.024,
+        0.09,
+        0.15,
+        0.024
+      ],
+      "is_above_sources": false,
+      "max_source_score": 0.15,
+      "scoreColor": "#ef4444"
+    },
+    "step_search_r2": {
+      "type": "step",
+      "query": "步骤3: 筛选并执行搜索 (筛选7个高分词,搜索7次,undefined个帖子)",
+      "level": 21,
+      "relevance_score": 0,
+      "strategy": "筛选并执行搜索",
+      "iteration": 2,
+      "is_selected": true
+    },
+    "search_制作表情包教程_r2_0": {
+      "type": "search_word",
+      "query": "[SEARCH] 制作表情包教程",
+      "level": 22,
+      "relevance_score": 0.6799999999999999,
+      "strategy": "搜索词",
+      "iteration": 2,
+      "is_selected": true
+    },
+    "post_67dc1a58000000001d0232da_0_2": {
+      "type": "post",
+      "query": "[R] 制作表情包教程3.0",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "67dc1a58000000001d0232da",
+      "note_url": "https://www.xiaohongshu.com/explore/67dc1a58000000001d0232da",
+      "body_text": "感谢大家的喜欢!顺便广广我的表情包双描边图源会 适合开会的宝宝使用 还有视频上跟我同款的预览框模板会 都是非常🥬哒!想",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g00831f8kdc165s505pt1o5hi2v0apnm0fv8?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831f8kdc165s505pt1o5hi2v0apnm0fv8?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 4899,
+        "collected_count": 2371,
+        "comment_count": 224,
+        "shared_count": 214
+      },
+      "extraction": null,
+      "is_knowledge": true,
+      "knowledge_reason": "帖子标题明确指出是“表情包教程”,正文提及“双描边图源”和“预览框模板”,这些都属于表情包制作的工具或技巧,可复用。",
+      "post_relevance_score": 0.55,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子提供了表情包制作的教程和相关素材/工具线索,与原始问题“如何制作表情包”的创作方法论有中度相关性。但帖子未涉及“猫咪”和“双标行为”这些具体内容主题,因此相关性不是高度直接。"
+    },
+    "post_68282bbd000000002001f349_0_3": {
+      "type": "post",
+      "query": "[R] 表情包P图素材抠图用",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "68282bbd000000002001f349",
+      "note_url": "https://www.xiaohongshu.com/explore/68282bbd000000002001f349",
+      "body_text": "1️⃣醒图导入以上表情图,放大截图就行没水印 2️⃣再导入一张自己的图片,把五官抠图下来放进表情图 3️⃣调节: 亮度",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g2sg31hitil85jie05oni2p56dljo632j3dg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hitil85jieg5oni2p56dljof6v1da8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hitil85jif05oni2p56dljoj488ivg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hitil85jifg5oni2p56dljo7r8c1s8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hitil85jig05oni2p56dljokvtrd08?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hitil85jigg5oni2p56dljocns0amo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hitil85jih05oni2p56dljo6mdks3g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hitil85jihg5oni2p56dljo0nmkpg8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hitil85jii05oni2p56dljoj4dqga0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hitil85jiig5oni2p56dljo0a13c88?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hitil85jie05oni2p56dljo632j3dg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hitil85jieg5oni2p56dljof6v1da8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hitil85jif05oni2p56dljoj488ivg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hitil85jifg5oni2p56dljo7r8c1s8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hitil85jig05oni2p56dljokvtrd08?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hitil85jigg5oni2p56dljocns0amo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hitil85jih05oni2p56dljo6mdks3g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hitil85jihg5oni2p56dljo0nmkpg8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hitil85jii05oni2p56dljoj4dqga0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hitil85jiig5oni2p56dljo0a13c88?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 3070,
+        "collected_count": 1581,
+        "comment_count": 67,
+        "shared_count": 314
+      },
+      "extraction": null,
+      "is_knowledge": true,
+      "knowledge_reason": "帖子提供了利用“醒图”App进行表情包P图的具体步骤和技巧,包括导入、截图、抠图、调整等,是可复用的工具使用和创作方法指导。",
+      "post_relevance_score": 0.4,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子提供了制作表情包的基础P图方法,但未涉及如何将猫咪形象与人类双标行为结合来表达特定主题,缺乏创意层面的指导,仅提供了工具性知识,与原始问题的核心目标(反映人类双标行为)相关性弱。"
+    },
+    "post_66ea4e30000000001e01964b_0_4": {
+      "type": "post",
+      "query": "[R] 教你DIY宝宝表情包~",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "66ea4e30000000001e01964b",
+      "note_url": "https://www.xiaohongshu.com/explore/66ea4e30000000001e01964b",
+      "body_text": "亲妈会做的有趣小事儿之一 拍很多宝宝的小表情,做成微信表情包😊 三步教你完成✅ 🉑美图秀秀,选择“图片美化",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g008317sg969u3u705o4jptig9f90d1cgkpo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008317sg969uju005o4jptig9f90779odk0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008317sg969uju0g5o4jptig9f90dr2v328?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008317sg969uju105o4jptig9f90dm7ka6o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008317sg969uju1g5o4jptig9f90sn9d1d8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008317sg969uju205o4jptig9f90o1h9l0o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008317sg969uju2g5o4jptig9f90juhvb88?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008317sg969u3u705o4jptig9f90d1cgkpo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008317sg969uju005o4jptig9f90779odk0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008317sg969uju0g5o4jptig9f90dr2v328?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008317sg969uju105o4jptig9f90dm7ka6o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008317sg969uju1g5o4jptig9f90sn9d1d8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008317sg969uju205o4jptig9f90o1h9l0o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008317sg969uju2g5o4jptig9f90juhvb88?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 2802,
+        "collected_count": 1506,
+        "comment_count": 66,
+        "shared_count": 1137
+      },
+      "extraction": null,
+      "is_knowledge": true,
+      "knowledge_reason": "帖子提供了制作表情包的具体步骤、使用的工具(美图秀秀)和操作流程(编辑、文字、添加),是可复用的创作方法和教程。",
+      "post_relevance_score": 0.5,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子提供了制作表情包的方法,这与原始问题“如何制作表情包梗图”这一通用需求有部分重叠。然而,它专注于宝宝表情包,且未涉及“猫咪”、“双标行为”等主题,因此相关性中等偏低。"
+    },
+    "post_6690ebe500000000250153e5_0_5": {
+      "type": "post",
+      "query": "[R] 微信自制个性表情包,太有意思了!",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "6690ebe500000000250153e5",
+      "note_url": "https://www.xiaohongshu.com/explore/6690ebe500000000250153e5",
+      "body_text": "#微信自制表情包    #微信小技巧    #微信设置    #微信表情    #手机技巧",
+      "images": [
+        "https://ci.xiaohongshu.com/spectrum/1040g0k0315571792gk105o4pqf008buj0d7qq30?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/spectrum/1040g0k0315571792gk105o4pqf008buj0d7qq30?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 1598,
+        "collected_count": 1201,
+        "comment_count": 30,
+        "shared_count": 172
+      },
+      "extraction": null,
+      "is_knowledge": true,
+      "knowledge_reason": "帖子标题和标签表明内容是关于“微信自制表情包”的方法或技巧,可能包含工具使用和操作步骤的指导。虽然没有看到具体内容,但其定位符合知识内容。",
+      "post_relevance_score": 0.4,
+      "relevance_level": "中度相关",
+      "relevance_reason": "该帖子与原始问题“如何制作反映人类双标行为的猫咪表情包梗图”仅在“表情包制作”这一宽泛概念上相关。帖子可能提供了表情包制作的通用方法,但未涉及“猫咪表情包”、“双标行为”或“梗图”的具体创作知识,参考价值有限。"
+    },
+    "post_671d9a72000000001402e50f_0_6": {
+      "type": "post",
+      "query": "[R] 小红书自制表情包!",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "671d9a72000000001402e50f",
+      "note_url": "https://www.xiaohongshu.com/explore/671d9a72000000001402e50f",
+      "body_text": "[飞吻R][飞吻R][飞吻R]#表情包制作  #小表情包  #表情包教程  #来更新了  [飞吻R][飞吻R][飞吻R",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g008319ejb3iv6a005nho0ea09dlcbau3dkg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008319ejb3iv6a0g5nho0ea09dlcifm9qng?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008319ejb3iv6a105nho0ea09dlcnmflne8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008319ejb3iv6a1g5nho0ea09dlciseqgb8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008319ejb3iv6a205nho0ea09dlcspj4d9o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008319ejb3iv6a2g5nho0ea09dlcu4ac7dg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008319ejb3iv6a305nho0ea09dlcr70th0g?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008319ejb3iv6a005nho0ea09dlcbau3dkg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008319ejb3iv6a0g5nho0ea09dlcifm9qng?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008319ejb3iv6a105nho0ea09dlcnmflne8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008319ejb3iv6a1g5nho0ea09dlciseqgb8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008319ejb3iv6a205nho0ea09dlcspj4d9o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008319ejb3iv6a2g5nho0ea09dlcu4ac7dg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008319ejb3iv6a305nho0ea09dlcr70th0g?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 653,
+        "collected_count": 403,
+        "comment_count": 1919,
+        "shared_count": 104
+      },
+      "extraction": null,
+      "is_knowledge": true,
+      "knowledge_reason": "该帖子通过图文结合的形式,清晰展示了在小红书平台内制作和添加自定义表情包的步骤和方法,属于可复用的工具使用教程。",
+      "post_relevance_score": 0.4,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子提供了制作表情包的通用方法,但未涉及如何将表情包内容设计成反映“人类双标行为”的主题,也与“猫咪”元素无关。它仅解决了表情包制作的平台操作层面问题,而非具体的创作内容层面问题。"
+    },
+    "post_668f5479000000000d00ebc2_0_7": {
+      "type": "post",
+      "query": "[R] 绝了!让人爆羡慕的技能(0成本💰无痛变厉害",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "668f5479000000000d00ebc2",
+      "note_url": "https://www.xiaohongshu.com/explore/668f5479000000000d00ebc2",
+      "body_text": "在此感谢我那个上架了好几系列表情包的闺蜜👯‍♀️她画的表情包系列有几百人使用!(而且半小时就把我教会了[哭惹R",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g2sg3153l9q6t1c7049ldg1no2qji4k25ge0?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3153l9q6t1c7049ldg1no2qji4k25ge0?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 77406,
+        "collected_count": 54710,
+        "comment_count": 770,
+        "shared_count": 3516
+      },
+      "extraction": null,
+      "is_knowledge": true,
+      "knowledge_reason": "帖子标题和图片明确提及“自制表情包”、“手把手教程”,正文也提到“半小时就把我教会了”,暗示发布者将分享表情包制作的方法或流程,是可复用的创作知识。",
+      "post_relevance_score": 0.5,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子内容与“制作表情包”相关,但未明确提及如何制作“反映人类双标行为的猫咪表情包梗图”。它可能提供制作表情包的通用方法,但缺乏针对特定主题(人类双标行为)和特定形象(猫咪)的指导,因此是中度相关。"
+    },
+    "post_680054bc000000001d014b19_0_8": {
+      "type": "post",
+      "query": "[R] 手把手教你用AI制作表情包‼️超详细教程",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "680054bc000000001d014b19",
+      "note_url": "https://www.xiaohongshu.com/explore/680054bc000000001d014b19",
+      "body_text": "① DeepSeek生成提示语 打开DeepSeek,输入要求,生成表情包【动作】及【对应文字】提示语。 如:生成20个",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g00831gc0ipb9ju705ne46t8g87jmvvg7dh8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831gc0ipb9ju7g5ne46t8g87jm12ha9g0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831gc0ipb9ju805ne46t8g87jmt09d8j8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831gc0ipb9ju8g5ne46t8g87jmko2jnu0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831gc0ipb9ju905ne46t8g87jmq8u02d8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831gc0ipb9ju9g5ne46t8g87jmiik9eg0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831gc0ipb9jua05ne46t8g87jmca5io40?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831gc0ipb9ju705ne46t8g87jmvvg7dh8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831gc0ipb9ju7g5ne46t8g87jm12ha9g0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831gc0ipb9ju805ne46t8g87jmt09d8j8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831gc0ipb9ju8g5ne46t8g87jmko2jnu0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831gc0ipb9ju905ne46t8g87jmq8u02d8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831gc0ipb9ju9g5ne46t8g87jmiik9eg0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831gc0ipb9jua05ne46t8g87jmca5io40?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 3166,
+        "collected_count": 3355,
+        "comment_count": 42,
+        "shared_count": 363
+      },
+      "extraction": null,
+      "is_knowledge": true,
+      "knowledge_reason": "帖子提供了一套使用AI工具(DeepSeek、即梦)和PS制作表情包的详细教程,包括生成提示语、图片制作、导出及后期处理的完整流程,是可复用的创作知识。",
+      "post_relevance_score": 0.5,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子提供了AI制作猫咪表情包的通用方法和工具,但原始问题特指“反映人类双标行为”,这个帖子没有提供如何将“双标行为”这一具体创意融入表情包设计的指导,因此相关性中等。"
+    },
+    "post_690818e70000000003011fa5_0_9": {
+      "type": "post",
+      "query": "[R] deepseek+即梦AI,人人都能制作表情包!",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "690818e70000000003011fa5",
+      "note_url": "https://www.xiaohongshu.com/explore/690818e70000000003011fa5",
+      "body_text": "用AI做表情包,不需要插画基础,只需几分钟,就能生成搞笑、有趣、个性化的表情包内容,快去试试。 #表情包制作  #AI",
+      "images": [
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odj8oc8l8005q7mur3dt625mvr33t0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odj8ooo6g005q7mur3dt625ivd643o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odhtusu5m2g5q7mur3dt625g3tc7s0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odj8pcrmg005q7mur3dt625nnq2fn0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odj8p5jl8005q7mur3dt6256beo5j8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odj8oukg6005q7mur3dt625d59fpu8?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odj8oc8l8005q7mur3dt625mvr33t0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odj8ooo6g005q7mur3dt625ivd643o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odhtusu5m2g5q7mur3dt625g3tc7s0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odj8pcrmg005q7mur3dt625nnq2fn0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odj8p5jl8005q7mur3dt6256beo5j8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odj8oukg6005q7mur3dt625d59fpu8?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 18,
+        "collected_count": 19,
+        "comment_count": 0,
+        "shared_count": 2
+      },
+      "extraction": null,
+      "is_knowledge": true,
+      "knowledge_reason": "帖子提供了详细的AI工具使用流程和步骤,包括提示词生成、图片生成、编辑导出等,属于可复用的创作方法和教程指导。",
+      "post_relevance_score": 0.55,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子提供了使用AI制作表情包的方法和工具,这与原始问题中“制作”表情包的需求相关。然而,它未直接解决如何反映“人类双标行为”这一具体创意主题,也未提供这方面的提示或技巧,因此属于中度相关,有一定参考价值但不够针对性。"
+    },
+    "search_怼双标的人表情包_r2_1": {
+      "type": "search_word",
+      "query": "[SEARCH] 怼双标的人表情包",
+      "level": 22,
+      "relevance_score": 0.655,
+      "strategy": "搜索词",
+      "iteration": 2,
+      "is_selected": true
+    },
+    "post_66af92ad0000000005038228_1_0": {
+      "type": "post",
+      "query": "[R] 来存怼人阴阳怪气的表情包了",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "66af92ad0000000005038228",
+      "note_url": "https://www.xiaohongshu.com/explore/66af92ad0000000005038228",
+      "body_text": "#发疯表情包  #道歉表情包  #斗图表情包  #每日表情包分享  #表情包  #聊天  #最爱的表情包  #阴阳怪气表",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g2sg31634rlb3had05ok8midoc9atv1jvlag?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31634rlb3ha805ok8midoc9ath15l07o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31634rlb3ha9g5ok8midoc9ath4tmtkg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31634rlb3ha7g5ok8midoc9atjbvqqr0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31634rlb3ha705ok8midoc9atpslav5g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31634rlb3haa05ok8midoc9atc65dg4o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31634rlb3ha8g5ok8midoc9at8fgjc30?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31634rlb3hac05ok8midoc9atfsetv3o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31634rlb3hab05ok8midoc9at9m1s7q8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31634soan1adg5ok8midoc9atotm2p18?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31634soan1ad05ok8midoc9atq6f5jr0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31634soan1abg5ok8midoc9atkjd6fj0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31634soan1aa05ok8midoc9atrk5i0do?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31634soan1aag5ok8midoc9atmshdusg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31634soan1a9g5ok8midoc9at9kerqi8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31634soan1acg5ok8midoc9atlgsmilg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31634soan1a705ok8midoc9atc342vgg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31634soan1a905ok8midoc9at4dnrujg?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31634rlb3had05ok8midoc9atv1jvlag?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31634rlb3ha805ok8midoc9ath15l07o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31634rlb3ha9g5ok8midoc9ath4tmtkg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31634rlb3ha7g5ok8midoc9atjbvqqr0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31634rlb3ha705ok8midoc9atpslav5g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31634rlb3haa05ok8midoc9atc65dg4o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31634rlb3ha8g5ok8midoc9at8fgjc30?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31634rlb3hac05ok8midoc9atfsetv3o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31634rlb3hab05ok8midoc9at9m1s7q8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31634soan1adg5ok8midoc9atotm2p18?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31634soan1ad05ok8midoc9atq6f5jr0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31634soan1abg5ok8midoc9atkjd6fj0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31634soan1aa05ok8midoc9atrk5i0do?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31634soan1aag5ok8midoc9atmshdusg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31634soan1a9g5ok8midoc9at9kerqi8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31634soan1acg5ok8midoc9atlgsmilg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31634soan1a705ok8midoc9atc342vgg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31634soan1a905ok8midoc9at4dnrujg?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 4491,
+        "collected_count": 1159,
+        "comment_count": 138,
+        "shared_count": 310
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子仅分享了一系列表情包图片,标题和正文均为标签,没有提供任何关于表情包制作的方法、技巧、工具或教程指导,不包含可复用的创作知识。",
+      "post_relevance_score": 0.1,
+      "relevance_level": "低度相关",
+      "relevance_reason": "该帖子展示的是阴阳怪气风格的表情包,但原始问题是关于“如何制作反映人类双标行为的猫咪表情包梗图”。帖子内容与问题中的“猫咪表情包”及“制作方法”无关,仅在“表情包”这一宽泛概念上有些许联系,参考价值非常有限。"
+    },
+    "post_68597fed000000001202d443_1_1": {
+      "type": "post",
+      "query": "[R] 🔥聊天怼人很好用的嘴替表情包 ​​​",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "68597fed000000001202d443",
+      "note_url": "https://www.xiaohongshu.com/explore/68597fed000000001202d443",
+      "body_text": "#表情包  #发疯表情包  #斗图表情包  #分享表情包  #搞笑表情包  #最爱的表情包  #奇奇怪怪表情包  #超适",
+      "images": [
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031j33322hhe6g4bc9gkosi9ikvkniovg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031j33322hhe604bc9gkosi9ikjnc8id0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831j3333pjh4004bc9gkosi9ik9kl13u8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031j33322hhe2g4bc9gkosi9ikit03vjo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831j3333gche004bc9gkosi9ikgpinseg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031j33322hhe204bc9gkosi9ik4i79pbg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031j33322hhe304bc9gkosi9ikecl7kmo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031j33322hhe4g4bc9gkosi9ik4mtp5a0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031j33322hhe404bc9gkosi9ik0t275q8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031j33322hhe504bc9gkosi9ikl28oaa0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831j3332jnhg004bc9gkosi9ik3hmb9k0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031j33322hhe5g4bc9gkosi9ikipu5mv0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831j3332eqha704bc9gkosi9ik5kl5od0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831j33329314004bc9gkosi9ik5oa1l60?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031j33322hhe6g4bc9gkosi9ikvkniovg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031j33322hhe604bc9gkosi9ikjnc8id0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831j3333pjh4004bc9gkosi9ik9kl13u8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031j33322hhe2g4bc9gkosi9ikit03vjo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831j3333gche004bc9gkosi9ikgpinseg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031j33322hhe204bc9gkosi9ik4i79pbg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031j33322hhe304bc9gkosi9ikecl7kmo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031j33322hhe4g4bc9gkosi9ik4mtp5a0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031j33322hhe404bc9gkosi9ik0t275q8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031j33322hhe504bc9gkosi9ikl28oaa0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831j3332jnhg004bc9gkosi9ik3hmb9k0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031j33322hhe5g4bc9gkosi9ikipu5mv0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831j3332eqha704bc9gkosi9ik5kl5od0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831j33329314004bc9gkosi9ik5oa1l60?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 93,
+        "collected_count": 23,
+        "comment_count": 2,
+        "shared_count": 4
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子仅分享了已制作好的表情包图片,不包含任何关于表情包制作的步骤、技巧或工具使用指南等可复用的创作知识。",
+      "post_relevance_score": 0.05,
+      "relevance_level": "低度相关",
+      "relevance_reason": "原始问题是关于“如何制作”反映人类双标行为的猫咪表情包梗图。该帖子虽然涉及表情包,但内容是现成表情包的分享,且没有猫咪表情包,更没有体现制作过程和双标行为,因此相关性极低,无法解决原始问题。"
+    },
+    "post_6541e7fb000000002303bfea_1_2": {
+      "type": "post",
+      "query": "[R] 真的很讨厌两面三刀的人,有事就当面说怂包",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "6541e7fb000000002303bfea",
+      "note_url": "https://www.xiaohongshu.com/explore/6541e7fb000000002303bfea",
+      "body_text": "#发疯日常",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g2sg30qu1pvp7h4eg5nh61ghg8g8l0d7fv20?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg30qu1pvp7h4hg5nh61ghg8g8lf0oqnng?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg30qu1pvp7h4g05nh61ghg8g8losgl9r0?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg30qu1pvp7h4eg5nh61ghg8g8l0d7fv20?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg30qu1pvp7h4hg5nh61ghg8g8lf0oqnng?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg30qu1pvp7h4g05nh61ghg8g8losgl9r0?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 1977,
+        "collected_count": 590,
+        "comment_count": 29,
+        "shared_count": 132
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子是纯粹的情感表达和个人吐槽,虽然使用了梗图形式,但未提供任何关于如何制作梗图、图片处理软件使用、表情包创作思路等可复用的创作方法或技巧。",
+      "post_relevance_score": 0.4,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子内容与原始问题“如何制作反映人类双标行为的猫咪表情包梗图”具有一定的概念相关性,因为帖子本身就在通过梗图表达对“两面三刀”(即双标行为)的厌恶,间接触及了双标这一主题。然而,帖子并未提供制作猫咪表情包的具体方法或创作指导,图片也不是猫咪表情包,因此参考价值有限,属于中度偏低相关。"
+    },
+    "post_67bb4d7d000000002903d2fb_1_3": {
+      "type": "post",
+      "query": "[R] 一些攻击力不是很强的怼人骂人表情包 ​​",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "67bb4d7d000000002903d2fb",
+      "note_url": "https://www.xiaohongshu.com/explore/67bb4d7d000000002903d2fb",
+      "body_text": "#我d图集  #表情包  #表情包分享  #发疯表情包  #聊天表情包  #实用表情包  #分享表情包  #搞笑表情包分",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g2sg31e8e5t780md05pin9e82ue9b6o9cr60?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31e8e5t780mc05pin9e82ue9bqt4rnu8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31e8e5t780ma05pin9e82ue9bra82in8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31e8e5t780m8g5pin9e82ue9bucua3c8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31e8e5t780m805pin9e82ue9b8g026b8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31e8e5t780m705pin9e82ue9btkpkad0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31e8e5t780mcg5pin9e82ue9bns5mcgo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31e8e5t780m905pin9e82ue9b9teefdo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31e8e5t780m9g5pin9e82ue9bbasi8mo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31e8e5t780mag5pin9e82ue9bdk5d6fo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31e8e5t780mb05pin9e82ue9b4ba47mg?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31e8e5t780md05pin9e82ue9b6o9cr60?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31e8e5t780mc05pin9e82ue9bqt4rnu8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31e8e5t780ma05pin9e82ue9bra82in8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31e8e5t780m8g5pin9e82ue9bucua3c8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31e8e5t780m805pin9e82ue9b8g026b8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31e8e5t780m705pin9e82ue9btkpkad0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31e8e5t780mcg5pin9e82ue9bns5mcgo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31e8e5t780m905pin9e82ue9b9teefdo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31e8e5t780m9g5pin9e82ue9bbasi8mo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31e8e5t780mag5pin9e82ue9bdk5d6fo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31e8e5t780mb05pin9e82ue9b4ba47mg?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 5008,
+        "collected_count": 1252,
+        "comment_count": 401,
+        "shared_count": 407
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子是一组表情包分享,不包含任何关于表情包制作的具体方法、技巧、工具使用或教程指导等可复用的创作知识。内容属于纯个人分享。",
+      "post_relevance_score": 0.1,
+      "relevance_level": "低度相关",
+      "relevance_reason": "该帖子与原始问题“如何制作反映人类双标行为的猫咪表情包梗图”相关性极低。帖子内容为怼人骂人表情包的分享,与猫咪主题和制作方法均不符。虽然都与表情包相关,但方向、主题和目的完全不同,无法提供任何有效参考。"
+    },
+    "post_6791c3e600000000170392b9_1_4": {
+      "type": "post",
+      "query": "[R] 回旋镖来得太快鸟!!!!",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "6791c3e600000000170392b9",
+      "note_url": "https://www.xiaohongshu.com/explore/6791c3e600000000170392b9",
+      "body_text": "才说光姐双标,马上轮到自家就说不出话了呢!! #光与夜之恋  #马蓝星表情包  ———— 🍊x",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g2sg31d01d147gkd05pkmf370u3ke98fc56g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831d01d14m0k6g5pkmf370u3ke7pl05rg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31d01d14mgmd05pkmf370u3keji6l8pg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831d01d14hgm6g5pkmf370u3kens0kfqo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831d01d14h106g5pkmf370u3keptu90no?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31d01d147gkd05pkmf370u3ke98fc56g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831d01d14m0k6g5pkmf370u3ke7pl05rg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31d01d14mgmd05pkmf370u3keji6l8pg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831d01d14hgm6g5pkmf370u3kens0kfqo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831d01d14h106g5pkmf370u3keptu90no?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 499,
+        "collected_count": 7,
+        "comment_count": 63,
+        "shared_count": 12
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子主要是一个个人分享的双标梗图,内容为博主对游戏角色的吐槽和自我调侃。帖子仅展示了梗图成品,并未提供任何关于如何制作梗图的方法、技巧、工具使用或详细步骤,不包含可复用的创作知识。",
+      "post_relevance_score": 0.5,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子中包含“双标”主题的可爱猫咪(或类似动物)表情包梗图,与原始问题中的“人类双标行为的猫咪表情包梗图”有一定内容上的契合。但帖子本身并未解答如何制作这类梗图,不包含创作方法或工具,因此无法直接解决原始问题,仅提供了一个示例或灵感,属于中度相关。"
+    },
+    "post_69080c43000000000402bb4b_1_5": {
+      "type": "post",
+      "query": "[R] 企鹅表情包 第二弹(素质怼人篇)",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "69080c43000000000402bb4b",
+      "note_url": "https://www.xiaohongshu.com/explore/69080c43000000000402bb4b",
+      "body_text": "小企鹅你咋这么全能 下次怼人就用这个攻击力很强的表情包[doge]#表情包演我精神状态  #有趣的表情包  #趣味表情包",
+      "images": [
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odec5pj065g5q794i6do35sh2bjcso?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odhnnhi6m605q794i6do35sb38fhdg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831odhnnkolm005q794i6do35sdrgij08?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831odhnnhkmg105q794i6do35sm9j3j48?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odhnnhi6m6g5q794i6do35s5jgbsmo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odhnng86m6g5q794i6do35sdlau8u8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odec5pj06005q794i6do35s8do921g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odec5pj06505q794i6do35sv8a5qvo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odec5pj060g5q794i6do35s8diish0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odec5pj06205q794i6do35summv4m0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odec5pj062g5q794i6do35sd4r8r8g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odec5pj06305q794i6do35sq97gmqg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odec5pj06105q794i6do35sb1rlk7g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odec5pj06405q794i6do35s6lndoo0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odec5pj063g5q794i6do35str0a7hg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odec5pj064g5q794i6do35s0l1i45o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odec5pj061g5q794i6do35s0cbohvo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odec5pj06605q794i6do35sf4vcpo0?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odec5pj065g5q794i6do35sh2bjcso?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odhnnhi6m605q794i6do35sb38fhdg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831odhnnkolm005q794i6do35sdrgij08?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831odhnnhkmg105q794i6do35sm9j3j48?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odhnnhi6m6g5q794i6do35s5jgbsmo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odhnng86m6g5q794i6do35sdlau8u8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odec5pj06005q794i6do35s8do921g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odec5pj06505q794i6do35sv8a5qvo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odec5pj060g5q794i6do35s8diish0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odec5pj06205q794i6do35summv4m0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odec5pj062g5q794i6do35sd4r8r8g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odec5pj06305q794i6do35sq97gmqg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odec5pj06105q794i6do35sb1rlk7g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odec5pj06405q794i6do35s6lndoo0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odec5pj063g5q794i6do35str0a7hg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odec5pj064g5q794i6do35s0l1i45o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odec5pj061g5q794i6do35s0cbohvo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031odec5pj06605q794i6do35sf4vcpo0?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 2410,
+        "collected_count": 727,
+        "comment_count": 45,
+        "shared_count": 480
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子主要分享了一组企鹅表情包,属于纯内容分享和情感表达,不包含表情包的创作方法、技巧、工具使用或制作流程等知识内容。",
+      "post_relevance_score": 0.1,
+      "relevance_level": "低度相关",
+      "relevance_reason": "原始问题是关于制作“反映人类双标行为的猫咪表情包梗图”的知识指导。该帖子提供的是企鹅表情包而非猫咪表情包,内容主题是“素质怼人”而非“双标行为”,且仅为表情包分享,不含任何制作方法,因此相关性极低。"
+    },
+    "post_66791a5f000000001c027fab_1_6": {
+      "type": "post",
+      "query": "[R] 反pua表情包合集",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "66791a5f000000001c027fab",
+      "note_url": "https://www.xiaohongshu.com/explore/66791a5f000000001c027fab",
+      "body_text": "",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g008314duls6g6g505n36gpa4ded8mv77e10?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008314duls6g6g5g5n36gpa4ded8lhoou5g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008314duls6g6g605n36gpa4ded8bek9fk8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008314duls6g6g6g5n36gpa4ded8e88j43g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg314dumpg8mg005n36gpa4ded8kmu654g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg314dumpg8mg0g5n36gpa4ded8q80gn20?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg314dumpg8mg105n36gpa4ded8g72pb9o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg314dumpg8mg1g5n36gpa4ded8vpeih98?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg314dumpg8mg205n36gpa4ded8e5nujoo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg314dumpg8mg2g5n36gpa4ded8acjit88?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg314dumpg8mg305n36gpa4ded8eq9r6qo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg314dumpg8mg3g5n36gpa4ded861tb2j8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg314dumpg8mg405n36gpa4ded8ul5iuro?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg314dumpg8mg4g5n36gpa4ded8v43q950?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg314dumpg8mg505n36gpa4ded808uo3f8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg314dumpg8mg5g5n36gpa4ded85gmhaj0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg314dumpg8mg605n36gpa4ded84lhc9c0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg314dumpg8mg6g5n36gpa4ded81dmga58?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008314duls6g6g505n36gpa4ded8mv77e10?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008314duls6g6g5g5n36gpa4ded8lhoou5g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008314duls6g6g605n36gpa4ded8bek9fk8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008314duls6g6g6g5n36gpa4ded8e88j43g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg314dumpg8mg005n36gpa4ded8kmu654g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg314dumpg8mg0g5n36gpa4ded8q80gn20?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg314dumpg8mg105n36gpa4ded8g72pb9o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg314dumpg8mg1g5n36gpa4ded8vpeih98?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg314dumpg8mg205n36gpa4ded8e5nujoo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg314dumpg8mg2g5n36gpa4ded8acjit88?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg314dumpg8mg305n36gpa4ded8eq9r6qo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg314dumpg8mg3g5n36gpa4ded861tb2j8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg314dumpg8mg405n36gpa4ded8ul5iuro?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg314dumpg8mg4g5n36gpa4ded8v43q950?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg314dumpg8mg505n36gpa4ded808uo3f8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg314dumpg8mg5g5n36gpa4ded85gmhaj0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg314dumpg8mg605n36gpa4ded84lhc9c0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg314dumpg8mg6g5n36gpa4ded81dmga58?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 1624,
+        "collected_count": 528,
+        "comment_count": 94,
+        "shared_count": 202
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子是纯粹的表情包分享合集,没有提供任何关于表情包制作的教程、方法、工具使用或设计理念等可复用的创作知识。内容属于成果展示而非知识传授。",
+      "post_relevance_score": 0.4,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子内容是表情包合集,其中最后一张图是猫咪表情包,且文字内容具有一定的双标或反讽意味,这与原始问题中“反映人类双标行为的猫咪表情包梗图”有间接关联。但帖子仅提供成品而非制作方法,不能直接解决问题,参考价值有限。"
+    },
+    "post_66e0e9b900000000270026f2_1_7": {
+      "type": "post",
+      "query": "[R] 阴阳怪气的怼人表情包!!!",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "66e0e9b900000000270026f2",
+      "note_url": "https://www.xiaohongshu.com/explore/66e0e9b900000000270026f2",
+      "body_text": "#斗图表情包  #奇奇怪怪的表情包又增加了  #奇奇怪怪表情包  #搞笑表情包  #可可爱爱表情包  #表情包  #表情",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g008317jaoplj460049jsaattabjetlgtf20?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008317jaoplj460g49jsaattabjebad4ccg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008317jaoplj461049jsaattabjeq7jauh8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008317jaoplj461g49jsaattabje6f7sfe8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008317jaoplj462049jsaattabje91eebu0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008317jaoplj462g49jsaattabje6tj4gp8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008317jaoplj463049jsaattabjehkf7pjg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008317jaoplj463g49jsaattabjeudun83o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008317jaoplj464049jsaattabje7mc5j00?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008317jaoplj460049jsaattabjetlgtf20?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008317jaoplj460g49jsaattabjebad4ccg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008317jaoplj461049jsaattabjeq7jauh8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008317jaoplj461g49jsaattabje6f7sfe8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008317jaoplj462049jsaattabje91eebu0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008317jaoplj462g49jsaattabje6tj4gp8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008317jaoplj463049jsaattabjehkf7pjg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008317jaoplj463g49jsaattabjeudun83o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008317jaoplj464049jsaattabje7mc5j00?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 7738,
+        "collected_count": 3331,
+        "comment_count": 179,
+        "shared_count": 2082
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "帖子属于纯粹的表情包分享,展示制作好的图片,不包含任何关于如何制作表情包梗图的方法、技巧或工具使用教程。",
+      "post_relevance_score": 0.45,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子展示的表情包梗图中有两张猫咪表情包内容间接涉及了“双标行为”(例如“从今天起,我要用圣人的标准要求别人,用贱人的标准来要求自己”),与原始问题的主题有轻微关联。但帖子主要聚焦展示现有表情包,而非教授制作方法,因此参考价值有限。"
+    },
+    "post_67db7618000000001c008ddb_1_8": {
+      "type": "post",
+      "query": "[R] 超实用哑口无言无语表情包(内附怼人金句)",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "67db7618000000001c008ddb",
+      "note_url": "https://www.xiaohongshu.com/explore/67db7618000000001c008ddb",
+      "body_text": "🌶“脑子是个日用品,不是装饰品,多用用吧。” 🌶“你家是开养殖场的吗?这么爱抬杠。” 🌶“我有密集恐惧症,不能接近",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g00831f7vprhl5u605olp88f6svjcri9rtg8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31f80d1ai6md05olp88f6svjc0n4bc0g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31f80d1ag6m6g5olp88f6svjckvd90a8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31f80d1ad5sdg5olp88f6svjc6518r28?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831f80d1abms6g5olp88f6svjc8hit79o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831f7vprhl5u2g5olp88f6svjcic8ftio?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831f7vprhl5u4g5olp88f6svjc89rc9qg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831f7vprhl5u0g5olp88f6svjcsupldng?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831f7vprhl5u305olp88f6svjcg9lvgso?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831f7vprhl5u005olp88f6svjcfhl97j8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831f7vprhl5u105olp88f6svjc4d9vsrg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831f7vprhl5u205olp88f6svjc4ooinbo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831f7vprhl5u1g5olp88f6svjcur3kjp8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831f7vprhl5u505olp88f6svjc31e0of8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831f7vprhl5u3g5olp88f6svjc1rlt5mg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831f7vprhl5u6g5olp88f6svjc5gca0b8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831f7vprhl5u405olp88f6svjc5i0t4i0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831f7vprhl5u5g5olp88f6svjcsaef308?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831f7vprhl5u605olp88f6svjcri9rtg8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31f80d1ai6md05olp88f6svjc0n4bc0g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31f80d1ag6m6g5olp88f6svjckvd90a8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31f80d1ad5sdg5olp88f6svjc6518r28?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831f80d1abms6g5olp88f6svjc8hit79o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831f7vprhl5u2g5olp88f6svjcic8ftio?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831f7vprhl5u4g5olp88f6svjc89rc9qg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831f7vprhl5u0g5olp88f6svjcsupldng?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831f7vprhl5u305olp88f6svjcg9lvgso?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831f7vprhl5u005olp88f6svjcfhl97j8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831f7vprhl5u105olp88f6svjc4d9vsrg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831f7vprhl5u205olp88f6svjc4ooinbo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831f7vprhl5u1g5olp88f6svjcur3kjp8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831f7vprhl5u505olp88f6svjc31e0of8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831f7vprhl5u3g5olp88f6svjc1rlt5mg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831f7vprhl5u6g5olp88f6svjc5gca0b8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831f7vprhl5u405olp88f6svjc5i0t4i0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831f7vprhl5u5g5olp88f6svjcsaef308?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 493,
+        "collected_count": 207,
+        "comment_count": 7,
+        "shared_count": 49
+      },
+      "extraction": null,
+      "is_knowledge": null,
+      "knowledge_reason": "",
+      "post_relevance_score": null,
+      "relevance_level": "",
+      "relevance_reason": ""
+    },
+    "post_66caccf2000000001f03bcee_1_9": {
+      "type": "post",
+      "query": "[R] 存 阴阳怪气表情包",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "66caccf2000000001f03bcee",
+      "note_url": "https://www.xiaohongshu.com/explore/66caccf2000000001f03bcee",
+      "body_text": "#发疯表情包  #搞笑表情包分享  #斗图表情包  #每日表情包分享  #可可爱爱表情包  阴阳怪气我是第一名",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g2sg316tnochl44704a6l35cu4nfhtfa8t60?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg316tnochl447g4a6l35cu4nfhkao4tpg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg316tnochl44804a6l35cu4nfh2to3f20?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg316tnochl448g4a6l35cu4nfh3c1vb98?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg316tnochl44904a6l35cu4nfhmltnqvo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg316tnochl449g4a6l35cu4nfhka5f9u0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg316tnochl44a04a6l35cu4nfhecc5ja0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg316tnochl44ag4a6l35cu4nfho2o2qc0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg316tnochl44b04a6l35cu4nfhgo9so30?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg316tnochl44bg4a6l35cu4nfhvco27vo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg316tnochl44c04a6l35cu4nfhdrlagio?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg316tnochl44cg4a6l35cu4nfh6njfg0o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg316tnochl44d04a6l35cu4nfhhugc0i0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg316tnochl44dg4a6l35cu4nfhq8noeno?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg316tnoci73m004a6l35cu4nfhpff9jg8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg316tnoci73m0g4a6l35cu4nfhd289h78?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg316tnoci73m104a6l35cu4nfhpldc610?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg316tnochl44704a6l35cu4nfhtfa8t60?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg316tnochl447g4a6l35cu4nfhkao4tpg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg316tnochl44804a6l35cu4nfh2to3f20?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg316tnochl448g4a6l35cu4nfh3c1vb98?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg316tnochl44904a6l35cu4nfhmltnqvo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg316tnochl449g4a6l35cu4nfhka5f9u0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg316tnochl44a04a6l35cu4nfhecc5ja0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg316tnochl44ag4a6l35cu4nfho2o2qc0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg316tnochl44b04a6l35cu4nfhgo9so30?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg316tnochl44bg4a6l35cu4nfhvco27vo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg316tnochl44c04a6l35cu4nfhdrlagio?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg316tnochl44cg4a6l35cu4nfh6njfg0o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg316tnochl44d04a6l35cu4nfhhugc0i0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg316tnochl44dg4a6l35cu4nfhq8noeno?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg316tnoci73m004a6l35cu4nfhpff9jg8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg316tnoci73m0g4a6l35cu4nfhd289h78?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg316tnoci73m104a6l35cu4nfhpldc610?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 12043,
+        "collected_count": 3022,
+        "comment_count": 232,
+        "shared_count": 953
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子仅分享了一组“阴阳怪气”主题的表情包,不包含任何关于表情包制作的方法、技巧、工具使用或教程指导等可复用的创作知识。",
+      "post_relevance_score": 0.2,
+      "relevance_level": "低度相关",
+      "relevance_reason": "该帖子提供了“阴阳怪气”主题的表情包,但原始问题是关于“制作反映人类双标行为的猫咪表情包梗图”。帖子内容尽管有“阴阳怪气”的共性,但没有猫咪元素,更未提供如何制作的知识/方法,也无法直接解决原始问题。"
+    },
+    "search_可爱又双标的表情包_r2_2": {
+      "type": "search_word",
+      "query": "[SEARCH] 可爱又双标的表情包",
+      "level": 22,
+      "relevance_score": 0.68,
+      "strategy": "搜索词",
+      "iteration": 2,
+      "is_selected": true
+    },
+    "post_690ec7070000000004022819_2_0": {
+      "type": "post",
+      "query": "[R] 小鸡毛双标时刻~专一的小鸡毛",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "690ec7070000000004022819",
+      "note_url": "https://www.xiaohongshu.com/explore/690ec7070000000004022819",
+      "body_text": "#线条小狗  #发给对象看  #小狗会永远爱你  #情侣表情包  #地球没有小狗不能转  #发给对象表情包  #小鸡毛",
+      "images": [
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831ok3vu6h6m705noss4h098t1ph5mlv8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831ok3vu6h6m7g5noss4h098t1upr9bkg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831ok3vu6h6m805noss4h098t18iosfjg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831ok3vu6h6m8g5noss4h098t1g6scm40?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831ok3vu6h6m905noss4h098t133cppgo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831ok3vu6h6m9g5noss4h098t15iplhh8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831ok3vu6h6ma05noss4h098t1i1d2ai8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831ok3vu6h6mag5noss4h098t1cn69a6o?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831ok3vu6h6m705noss4h098t1ph5mlv8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831ok3vu6h6m7g5noss4h098t1upr9bkg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831ok3vu6h6m805noss4h098t18iosfjg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831ok3vu6h6m8g5noss4h098t1g6scm40?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831ok3vu6h6m905noss4h098t133cppgo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831ok3vu6h6m9g5noss4h098t15iplhh8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831ok3vu6h6ma05noss4h098t1i1d2ai8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831ok3vu6h6mag5noss4h098t1cn69a6o?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 1760,
+        "collected_count": 361,
+        "comment_count": 385,
+        "shared_count": 3367
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子是一组卡通小狗表情包,内容是纯粹的情感表达和娱乐分享,不包含任何关于表情包制作的方法、技巧、工具使用或教程指导等创作知识。",
+      "post_relevance_score": 0.4,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子内容是关于“双标”主题的表情包,与原始问题“如何制作反映人类双标行为的猫咪表情包梗图”在主题概念上有所关联。然而,帖子展示的是“小狗”表情包,而非“猫咪”,且未提供任何制作方法或创作指导,因此参考价值有限,属于中度偏低相关。"
+    },
+    "post_66877a00000000000a0073d7_2_1": {
+      "type": "post",
+      "query": "[R] 我的双标时刻表情包",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "66877a00000000000a0073d7",
+      "note_url": "https://www.xiaohongshu.com/explore/66877a00000000000a0073d7",
+      "body_text": "薛定谔的挑食[萌萌哒R] #吃货  #吃货薯看这里  #表情包  #可爱表情包  #搞笑  #梗图  #芒果  #薯条",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g2sg314rvsagl1edg5noui3c08sqi5fgd8j0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg314rvsagl1ec05noui3c08sqinpaljcg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg314rvsagl1ebg5noui3c08sqi9rjq7d0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg314rvsagl1ecg5noui3c08sqio48ga6o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg314rvsagl1eb05noui3c08sqioildsuo?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg314rvsagl1edg5noui3c08sqi5fgd8j0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg314rvsagl1ec05noui3c08sqinpaljcg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg314rvsagl1ebg5noui3c08sqi9rjq7d0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg314rvsagl1ecg5noui3c08sqio48ga6o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg314rvsagl1eb05noui3c08sqioildsuo?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 38000,
+        "collected_count": 4938,
+        "comment_count": 4802,
+        "shared_count": 20130
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子主要展示了作者制作的猫咪双标表情包及日常分享,不包含可复用的创作方法、技巧或教程指导等知识内容。",
+      "post_relevance_score": 0.8,
+      "relevance_level": "高度相关",
+      "relevance_reason": "帖子直接展示了如何以猫咪为主题来表达“双标行为”的表情包梗图,与原始问题“如何制作反映人类双标行为的猫咪表情包梗图”高度契合。虽然没有提供具体的制作教程,但其内容本身就是对问题的一种直接答案示范,提供了创作方向和视觉灵感。"
+    },
+    "post_68ae7e48000000001c004610_2_2": {
+      "type": "post",
+      "query": "[R] 可爱又双标的表情包27.0",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "68ae7e48000000001c004610",
+      "note_url": "https://www.xiaohongshu.com/explore/68ae7e48000000001c004610",
+      "body_text": "快发给你那即美丽又双标的姊妹吧#表情包演我精神状态  #每日表情包分享  #饭搭子  #实用表情包  #情侣表情包",
+      "images": [
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031lm33318lm005nddjvo08884om9osc8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031lm33318lm0g5nddjvo0888437kibao?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031lm33318lm105nddjvo088845ner52g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031lm33318lm1g5nddjvo08884gdrnkq0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031lm33318lm205nddjvo08884d0n4bto?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031lm33318lm2g5nddjvo08884999uq0o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031lm33318lm305nddjvo08884nldf1mo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031lm33318lm3g5nddjvo08884pansvb8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031lm33318lm405nddjvo08884q3bm9oo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031lm33318lm4g5nddjvo08884euegjr0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031lm33318lm505nddjvo08884rintmcg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031lm33318lm5g5nddjvo08884qo6e5l8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031lm33318lm605nddjvo08884il3i978?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031lm33318lm6g5nddjvo08884270l0jo?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031lm33318lm005nddjvo08884om9osc8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031lm33318lm0g5nddjvo0888437kibao?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031lm33318lm105nddjvo088845ner52g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031lm33318lm1g5nddjvo08884gdrnkq0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031lm33318lm205nddjvo08884d0n4bto?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031lm33318lm2g5nddjvo08884999uq0o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031lm33318lm305nddjvo08884nldf1mo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031lm33318lm3g5nddjvo08884pansvb8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031lm33318lm405nddjvo08884q3bm9oo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031lm33318lm4g5nddjvo08884euegjr0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031lm33318lm505nddjvo08884rintmcg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031lm33318lm5g5nddjvo08884qo6e5l8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031lm33318lm605nddjvo08884il3i978?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031lm33318lm6g5nddjvo08884270l0jo?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 20,
+        "collected_count": 9,
+        "comment_count": 0,
+        "shared_count": 2
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子是一系列表情包的分享,没有提供任何关于表情包制作的方法、技巧、工具使用或流程教程,不包含可复用的创作知识。",
+      "post_relevance_score": 0.05,
+      "relevance_level": "低度相关",
+      "relevance_reason": "原始问题是关于“如何制作”反映双标行为的猫咪表情包梗图,而该帖子是“分享”可爱的表情包,且图片是人类幼崽而非猫咪。内容与问题描述的创作主题和对象均不符,不涉及制作方法或相关工具,参考价值极低。"
+    },
+    "post_6697b8310000000025003164_2_3": {
+      "type": "post",
+      "query": "[R] 豚的大型双标现场!",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "6697b8310000000025003164",
+      "note_url": "https://www.xiaohongshu.com/explore/6697b8310000000025003164",
+      "body_text": "我:说好不喜欢吃的呢!![抓狂R][抓狂R][抓狂R] 豚豚:嘿嘿[萌萌哒R]其实我都爱吃~[吐舌头H][吐舌头H",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g2sg315brfaqk0s005p1vc53qaovb7c0nmt8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg315brfaqk0s0g5p1vc53qaovb5fbb4q8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg315brfaqk0s105p1vc53qaovbnuj9n2g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg315brfaqk0s1g5p1vc53qaovbkks6p00?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg315brfaqk0s005p1vc53qaovb7c0nmt8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg315brfaqk0s0g5p1vc53qaovb5fbb4q8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg315brfaqk0s105p1vc53qaovbnuj9n2g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg315brfaqk0s1g5p1vc53qaovbkks6p00?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 2967,
+        "collected_count": 1778,
+        "comment_count": 100,
+        "shared_count": 298
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子通过一系列插画展示了卡皮巴拉“双标”的行为,内容为纯个人分享和情感表达,未提供任何关于制作表情包梗图的方法、技巧或工具使用等可复用的创作知识。",
+      "post_relevance_score": 0.75,
+      "relevance_level": "高度相关",
+      "relevance_reason": "帖子内容与原始问题“如何制作反映人类双标行为的猫咪表情包梗图”具有中高度相关性。尽管不是猫咪,而是卡皮巴拉,但它生动展示了“双标行为”这一核心概念,并以“表情包梗图”的形式呈现。观看此帖可激发创作灵感,理解双标行为的表达方式,但未提供制作指南。因此,有一定的参考价值,但缺乏技术指导。"
+    },
+    "post_68626478000000000d019251_2_4": {
+      "type": "post",
+      "query": "[R] 多栋对食物的双标日常",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "68626478000000000d019251",
+      "note_url": "https://www.xiaohongshu.com/explore/68626478000000000d019251",
+      "body_text": "#暹罗厘普  #比格多栋  #比格多栋是最棒的小狗  #比格多栋才是众望所归  #表情包  #我最爱的表情包  #双标狗",
+      "images": [
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031jbmq1oi360g5o9lqlq7vqa9daaonc8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031jbmq1oi361g5o9lqlq7vqa94ndmp60?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031jbotkod36505o9lqlq7vqa9gaoqvs8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031jbotkod366g5o9lqlq7vqa9qdf8tm0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031jbmq1oi36005o9lqlq7vqa9hgblsho?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031jbmq1oi366g5o9lqlq7vqa9bqb27uo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031jbmq1oi36405o9lqlq7vqa94elsf50?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031jbmq1oi360g5o9lqlq7vqa9daaonc8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031jbmq1oi361g5o9lqlq7vqa94ndmp60?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031jbotkod36505o9lqlq7vqa9gaoqvs8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031jbotkod366g5o9lqlq7vqa9qdf8tm0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031jbmq1oi36005o9lqlq7vqa9hgblsho?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031jbmq1oi366g5o9lqlq7vqa9bqb27uo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031jbmq1oi36405o9lqlq7vqa94elsf50?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 38154,
+        "collected_count": 4300,
+        "comment_count": 2876,
+        "shared_count": 18491
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子是一系列关于比格犬对食物双标的梗图,属于日常分享和情感表达,不包含可复用的创作方法、技巧、工具使用或流程指导等知识内容。",
+      "post_relevance_score": 0.5,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子内容通过图片形式表现了“双标”行为,与原始问题中的“反映人类双标行为”有概念上的重叠。但原始问题是关于“制作猫咪表情包梗图”,而帖子是关于狗的,且未提供任何制作方法或创作指导,因此相关性中等,仅有主题上的关联。"
+    },
+    "post_68dde20600000000070173ef_2_5": {
+      "type": "post",
+      "query": "[R] 画了一些新的表情包",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "68dde20600000000070173ef",
+      "note_url": "https://www.xiaohongshu.com/explore/68dde20600000000070173ef",
+      "body_text": "#表情包  #自制表情包  #小表情  #原创表情包  #表情包分享",
+      "images": [
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031n4c02g052505pmcamhjcser0m84koo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031n4c02g0523g5pmcamhjcserr538rbg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031n4c02g052205pmcamhjcserd5f07c0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031n4c02g0522g5pmcamhjcser9u6itvo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031n4c02g052305pmcamhjcserpsiatro?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031n4c02g052405pmcamhjcser4cr25pg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031n4c02g0524g5pmcamhjcser9lu8vmo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031n4c02g0525g5pmcamhjcserfb33c9g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031n4c02g052605pmcamhjcser31amku0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031n4c02g0526g5pmcamhjcserg1g3sa8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831n4c2c64kq7g5pmcamhjcserqk4fjh0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831n4c2c64kq705pmcamhjcserji5ruio?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831n4c2c64kq805pmcamhjcserkc1t240?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831n4c2c64kq8g5pmcamhjcser6k5tnlo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831n4c2c64kq905pmcamhjcseroih3am8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831n4c2c64kqa05pmcamhjcserknavu68?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831n4c2c64kq9g5pmcamhjcsermngsjg0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831n4c2c64kqag5pmcamhjcserv5ov4no?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031n4c02g052505pmcamhjcser0m84koo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031n4c02g0523g5pmcamhjcserr538rbg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031n4c02g052205pmcamhjcserd5f07c0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031n4c02g0522g5pmcamhjcser9u6itvo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031n4c02g052305pmcamhjcserpsiatro?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031n4c02g052405pmcamhjcser4cr25pg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031n4c02g0524g5pmcamhjcser9lu8vmo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031n4c02g0525g5pmcamhjcserfb33c9g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031n4c02g052605pmcamhjcser31amku0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031n4c02g0526g5pmcamhjcserg1g3sa8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831n4c2c64kq7g5pmcamhjcserqk4fjh0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831n4c2c64kq705pmcamhjcserji5ruio?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831n4c2c64kq805pmcamhjcserkc1t240?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831n4c2c64kq8g5pmcamhjcser6k5tnlo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831n4c2c64kq905pmcamhjcseroih3am8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831n4c2c64kqa05pmcamhjcserknavu68?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831n4c2c64kq9g5pmcamhjcsermngsjg0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831n4c2c64kqag5pmcamhjcserv5ov4no?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 4404,
+        "collected_count": 899,
+        "comment_count": 237,
+        "shared_count": 282
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子仅分享了个人创作的表情包成品,未提供任何关于表情包制作的方法、技巧、工具使用或详细步骤,不包含可复用的创作知识。",
+      "post_relevance_score": 0.3,
+      "relevance_level": "低度相关",
+      "relevance_reason": "该帖子展示的是猫咪形象的表情包,与问题中“猫咪表情包”有形象上的关联,但其内容是纯表情包展示,并未涉及“反映人类双标行为”这一主题,也未提供任何制作表情包梗图的创作知识或方法,因此相关性较低。"
+    },
+    "post_67dd3945000000001d02d900_2_6": {
+      "type": "post",
+      "query": "[R] 我的双标时刻!",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "67dd3945000000001d02d900",
+      "note_url": "https://www.xiaohongshu.com/explore/67dd3945000000001d02d900",
+      "body_text": "#双标现场  #双标  #人的本质是双标  #有被内涵到  #打工人日常  #懒人  #表情包  #吃货  #官方助推",
+      "images": [
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831f9hk311me7g5ps76s5i3bmbbvkhmn8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831f9hk311me9g5ps76s5i3bmb98rnhag?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831f9hk311me805ps76s5i3bmb2r6730g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831f9hk311me8g5ps76s5i3bmb9tlcif8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831f9hk311me905ps76s5i3bmbu5ehb8o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831f9hk311mea05ps76s5i3bmbect83ro?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831f9hk311me7g5ps76s5i3bmbbvkhmn8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831f9hk311me9g5ps76s5i3bmb98rnhag?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831f9hk311me805ps76s5i3bmb2r6730g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831f9hk311me8g5ps76s5i3bmb9tlcif8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831f9hk311me905ps76s5i3bmbu5ehb8o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831f9hk311mea05ps76s5i3bmbect83ro?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 2861,
+        "collected_count": 458,
+        "comment_count": 525,
+        "shared_count": 3819
+      },
+      "extraction": null,
+      "is_knowledge": null,
+      "knowledge_reason": "",
+      "post_relevance_score": null,
+      "relevance_level": "",
+      "relevance_reason": ""
+    },
+    "post_6907666b0000000004022dbb_2_7": {
+      "type": "post",
+      "query": "[R] 表情包分享|威廉企鹅自恋版",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "6907666b0000000004022dbb",
+      "note_url": "https://www.xiaohongshu.com/explore/6907666b0000000004022dbb",
+      "body_text": "🐧🐧🐧 图源@早柚 #有趣的表情包  #企鹅  #每日表情包分享  #禁止搬运 #会火  #薯队长",
+      "images": [
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octa2rg062g5q1qqkn6se2be4282no?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octa2rg06405q1qqkn6se2ba0sa6m0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octfraf06105q1qqkn6se2bh2k1g70?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octfraf06505q1qqkn6se2bnd96h18?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octfraf066g5q1qqkn6se2b8lovrb8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octfraf065g5q1qqkn6se2bqc9e56g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octfr83g6605q1qqkn6se2b3h1dh6g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octfraf06605q1qqkn6se2b2rmrqn8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octfr83g66g5q1qqkn6se2b5b6ogso?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octa2rg06005q1qqkn6se2btl8umjo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octa2rg061g5q1qqkn6se2bd2ntipg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octa2rg060g5q1qqkn6se2b88m0lmg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octa2rg06105q1qqkn6se2bhrj3di8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octa2rg063g5q1qqkn6se2bai7d700?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octa2rg06305q1qqkn6se2bkuu22a0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octa2rg06205q1qqkn6se2b2gr424o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octa2rg06605q1qqkn6se2bvb0uv6g?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octa2rg062g5q1qqkn6se2be4282no?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octa2rg06405q1qqkn6se2ba0sa6m0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octfraf06105q1qqkn6se2bh2k1g70?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octfraf06505q1qqkn6se2bnd96h18?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octfraf066g5q1qqkn6se2b8lovrb8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octfraf065g5q1qqkn6se2bqc9e56g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octfr83g6605q1qqkn6se2b3h1dh6g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octfraf06605q1qqkn6se2b2rmrqn8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octfr83g66g5q1qqkn6se2b5b6ogso?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octa2rg06005q1qqkn6se2btl8umjo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octa2rg061g5q1qqkn6se2bd2ntipg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octa2rg060g5q1qqkn6se2b88m0lmg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octa2rg06105q1qqkn6se2bhrj3di8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octa2rg063g5q1qqkn6se2bai7d700?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octa2rg06305q1qqkn6se2bkuu22a0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octa2rg06205q1qqkn6se2b2gr424o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031octa2rg06605q1qqkn6se2bvb0uv6g?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 3139,
+        "collected_count": 881,
+        "comment_count": 38,
+        "shared_count": 708
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子仅分享了一组现成的企鹅表情包图片,没有提供任何关于表情包制作的方法、技巧、工具使用步骤或教程指导,不包含可复用的创作知识。",
+      "post_relevance_score": 0.1,
+      "relevance_level": "低度相关",
+      "relevance_reason": "原始问题是关于“如何制作反映人类双标行为的猫咪表情包梗图”的方法,而该帖子仅是分享企鹅表情包而非猫咪,且未提供任何制作方法或双标行为相关的创作思路,因此相关性极低。"
+    },
+    "post_68a43430000000001d0027d6_2_8": {
+      "type": "post",
+      "query": "[R] 比格多栋的双标时刻:",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "68a43430000000001d0027d6",
+      "note_url": "https://www.xiaohongshu.com/explore/68a43430000000001d0027d6",
+      "body_text": "#萌物  #梗图  #梗图系列  #此图有梗  #暹罗厘普  #比格多栋  #张田中  #兔子秀珍  #人的本质是双标",
+      "images": [
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831lbpdv3vkubg5o9lqlq7vqa9b21cqv0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831lbpdv3vku9g5o9lqlq7vqa9ibu6p8o?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831lbpdv3vkubg5o9lqlq7vqa9b21cqv0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831lbpdv3vku9g5o9lqlq7vqa9ibu6p8o?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 1448,
+        "collected_count": 116,
+        "comment_count": 90,
+        "shared_count": 586
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "帖子内容为发布者创作的梗图展示,纯属个人分享和情感表达,不包含任何创作方法、技巧、工具使用或流程指导等可复用的知识内容。",
+      "post_relevance_score": 0.4,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子展示的梗图确实反映了“双标”这一主题,并在标题和标签中提及,与原始问题“如何制作反映人类双标行为的猫咪表情包梗图”中的“双标行为”和“梗图”有一定概念上的重叠。但图片内容是狗,而非猫,且未提供关于如何制作梗图的任何指导或方法,因此参考价值有限。"
+    },
+    "post_67d2e30d000000000603eb03_2_9": {
+      "type": "post",
+      "query": "[R] 真的好喜欢这个表情包啊",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "67d2e30d000000000603eb03",
+      "note_url": "https://www.xiaohongshu.com/explore/67d2e30d000000000603eb03",
+      "body_text": "一些爱用表情包,谁会不喜欢可爱猫猫呢#表情包分享  #搞笑表情包  #聊天表情包  #可爱表情包  #猫咪表情包  #猫",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g00831evkendj6a605on0j475al80hr6h0m8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831evkenhrme6g5on0j475al80lksdmbg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831evkenhj6a6g5on0j475al80d6ccjt0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831evkenhbmi5g5on0j475al80ck4knqo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831evkenf166605on0j475al8053lfo3g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831evkenebmi5g5on0j475al80tl294l0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831evkeneb6a605on0j475al80td1gpf8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31evkenecmocg5on0j475al80d8tc778?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831evkene9ma6g5on0j475al80dccljs8?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831evkendj6a605on0j475al80hr6h0m8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831evkenhrme6g5on0j475al80lksdmbg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831evkenhj6a6g5on0j475al80d6ccjt0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831evkenhbmi5g5on0j475al80ck4knqo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831evkenf166605on0j475al8053lfo3g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831evkenebmi5g5on0j475al80tl294l0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831evkeneb6a605on0j475al80td1gpf8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31evkenecmocg5on0j475al80d8tc778?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831evkene9ma6g5on0j475al80dccljs8?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 10582,
+        "collected_count": 2362,
+        "comment_count": 731,
+        "shared_count": 841
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子仅为猫咪表情包的纯分享,提供了9张现成的表情包图片及一些标签,并未包含任何关于如何制作表情包的方法、技巧、步骤或工具使用等创作知识。",
+      "post_relevance_score": 0.1,
+      "relevance_level": "低度相关",
+      "relevance_reason": "原始问题是关于“如何制作反映人类双标行为的猫咪表情包梗图”,而帖子只分享了一些可爱的猫咪表情包,其中甚至没有明确体现“双标行为”的梗图,更没有提供制作方法,因此相关性极低。"
+    },
+    "search_梗图描改教程_r2_3": {
+      "type": "search_word",
+      "query": "[SEARCH] 梗图描改教程",
+      "level": 22,
+      "relevance_score": 0.705,
+      "strategy": "搜索词",
+      "iteration": 2,
+      "is_selected": true
+    },
+    "post_68ef8753000000000301d581_3_0": {
+      "type": "post",
+      "query": "[R] 奇美拉都能学会的让描改meme更有感觉教程",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "68ef8753000000000301d581",
+      "note_url": "https://www.xiaohongshu.com/explore/68ef8753000000000301d581",
+      "body_text": "——以五个超级讨厌mikuOMG和软件sai2为例 也可以当成氛围头万金油教程(……) #绘画过程  #绘画教程  #绘",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g2sg31nlj712klmkg5p191obkcdnbufe8jj8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nljfuit6g605p191obkcdnbdrekct8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nljfuit6g3g5p191obkcdnbjf5o55g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nljfuit6g205p191obkcdnb3qmi2mg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nljfuit6g2g5p191obkcdnb25nn8b8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nljfuit6g405p191obkcdnbpav08lo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nljfuit6g6g5p191obkcdnbc329djo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nljfuit6g4g5p191obkcdnbbemu818?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nljfuit6g5g5p191obkcdnbdhh5msg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nljfuit6g505p191obkcdnbj0v7oa0?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31nlj712klmkg5p191obkcdnbufe8jj8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nljfuit6g605p191obkcdnbdrekct8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nljfuit6g3g5p191obkcdnbjf5o55g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nljfuit6g205p191obkcdnb3qmi2mg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nljfuit6g2g5p191obkcdnb25nn8b8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nljfuit6g405p191obkcdnbpav08lo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nljfuit6g6g5p191obkcdnbc329djo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nljfuit6g4g5p191obkcdnbbemu818?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nljfuit6g5g5p191obkcdnbdhh5msg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nljfuit6g505p191obkcdnbj0v7oa0?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 12520,
+        "collected_count": 6189,
+        "comment_count": 52,
+        "shared_count": 491
+      },
+      "extraction": null,
+      "is_knowledge": true,
+      "knowledge_reason": "帖子提供了详细的描改meme制作教程,包括线稿、上色、添加背景、调整光影等多个步骤以及软件使用技巧,属于可复用的创作方法和流程指导。",
+      "post_relevance_score": 0.5,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子提供了制作表情包梗图的描改技术和后期处理方法,对'如何制作反映人类双标行为的猫咪表情包梗图'中的'制作表情包梗图'部分有指导作用。然而,它并未涉及'反映人类双标行为'或'猫咪'等主题内容,因此相关性为中等。"
+    },
+    "post_6882582c000000000d018245_3_1": {
+      "type": "post",
+      "query": "[R] 一个很抽象的教程",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "6882582c000000000d018245",
+      "note_url": "https://www.xiaohongshu.com/explore/6882582c000000000d018245",
+      "body_text": "我来了,有啥不会的评论区问吧😁 #oc  #未经允许禁止转载二次修改  #禁ai  #约稿展示  #自设  #毫无技术",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g2sg31karqrsj3ql05pqo9c8ndpcillbda48?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31karqrsj3ql05pqo9c8ndpcillbda48?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 555,
+        "collected_count": 98,
+        "comment_count": 77,
+        "shared_count": 18
+      },
+      "extraction": null,
+      "is_knowledge": true,
+      "knowledge_reason": "帖子标题和图片明确提及“梗图教程”,正文虽短但表示“有啥不会的评论区问吧”,暗示可以提供创作方法或指导。虽然内容本身没展开,但形式上是提供了知识分享的意图。",
+      "post_relevance_score": 0.4,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子标题和图片指出是“梗图教程”,与原始问题“如何制作…表情包梗图”的创作形式相关。但帖子内容并未涉及或展示任何猫咪元素或双标行为,也未给出具体教程,与特定主题关联性极弱。"
+    },
+    "post_689937da0000000022023e1e_3_2": {
+      "type": "post",
+      "query": "[R] 梗图描改模板",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "689937da0000000022023e1e",
+      "note_url": "https://www.xiaohongshu.com/explore/689937da0000000022023e1e",
+      "body_text": "二改随意#指绘  #梗图  #模板",
+      "images": [
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031l1a92ksiu5g5o1qs6j08u7k7urg0h8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031l1a92u2304g5o1qs6j08u7kg20maeg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031l1a92u2302g5o1qs6j08u7kuv6ta28?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031l1a92u230305o1qs6j08u7kc1bi1rg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031l1a92u230205o1qs6j08u7kadbl8ig?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031l1a92ksiu5g5o1qs6j08u7k7urg0h8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031l1a92u2304g5o1qs6j08u7kg20maeg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031l1a92u2302g5o1qs6j08u7kuv6ta28?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031l1a92u230305o1qs6j08u7kc1bi1rg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031l1a92u230205o1qs6j08u7kadbl8ig?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 1250,
+        "collected_count": 350,
+        "comment_count": 105,
+        "shared_count": 59
+      },
+      "extraction": null,
+      "is_knowledge": true,
+      "knowledge_reason": "帖子提供了梗图描改的模板,即一种可复用的创作工具或素材,用户可以基于此进行二次创作,属于制作梗图的方法之一。",
+      "post_relevance_score": 0.4,
+      "relevance_level": "中度相关",
+      "relevance_reason": "该帖子提供了梗图模板,这对于制作梗图具有一定的参考价值,但它没有直接涉及“猫咪表情包”和“人类双标行为”这两个核心元素,需要用户进一步创造性地填充内容。因此,相关性中等偏低。"
+    },
+    "post_68fdde910000000003018b60_3_3": {
+      "type": "post",
+      "query": "[R] 神犬妙探巴布洛梗图描改(5/)",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "68fdde910000000003018b60",
+      "note_url": "https://www.xiaohongshu.com/explore/68fdde910000000003018b60",
+      "body_text": "加百列:(耶) 依旧是@我想画稿 老师画的😋 #神犬妙探巴布洛  #同人产粮节  #好奇心档案馆  #加百列",
+      "images": [
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031o3j1igplm305pmc3nm2u2vnlr10lu8?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031o3j1igplm305pmc3nm2u2vnlr10lu8?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 52,
+        "collected_count": 6,
+        "comment_count": 8,
+        "shared_count": 0
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子是一张描改的梗图分享,内容为二次创作和情感表达,不包含任何可复用的创作方法、技巧或指导,无法提供面向创作的知识。",
+      "post_relevance_score": 0.05,
+      "relevance_level": "低度相关",
+      "relevance_reason": "原始问题是关于制作猫咪表情包梗图以反映人类双标行为,而该帖子内容是《神犬妙探巴布洛》的同人描改梗图,主角为动漫人物,主题与猫咪和双标行为无关。因此相关性极低,无法解决原始问题。"
+    },
+    "post_63cfabdd000000001b0173d5_3_4": {
+      "type": "post",
+      "query": "[R] 描改梗图过程。",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "63cfabdd000000001b0173d5",
+      "note_url": "https://www.xiaohongshu.com/explore/63cfabdd000000001b0173d5",
+      "body_text": "#爱官方官方大大求热门  #查理九世  #绘画过程  #描改致敬@🎋是墨的恋爱对手la",
+      "images": [
+        "https://ci.xiaohongshu.com/1000g0081uhvsmsef00605oq1cemm54cdiupk2n0?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1000g0081uhvsmsef00605oq1cemm54cdiupk2n0?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 75,
+        "collected_count": 14,
+        "comment_count": 1,
+        "shared_count": 1
+      },
+      "extraction": null,
+      "is_knowledge": true,
+      "knowledge_reason": "帖子标题和标签提及“描改梗图过程”和“绘画过程”,暗示其可能包含描改梗图或绘画的步骤或技巧,具有可复用的创作指导价值。",
+      "post_relevance_score": 0.4,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子内容提及“描改梗图过程”,与制作表情包梗图在一定程度上有方法上的关联,但与“猫咪表情包”和“人类双标行为”这两个核心元素关联性弱,不直接解决原问题。"
+    },
+    "post_62e4f79a000000000e0308ed_3_5": {
+      "type": "post",
+      "query": "[R] 描改教程(细讲线稿)",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "62e4f79a000000000e0308ed",
+      "note_url": "https://www.xiaohongshu.com/explore/62e4f79a000000000e0308ed",
+      "body_text": "#描改致歉  #喜羊羊与灰太狼  #教程",
+      "images": [
+        "https://ci.xiaohongshu.com/01027u01ki6ksriwfi701113kdfbp6wk3v?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/01027u01ki6ksriwfi701113kdfbp6wk3v?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 716,
+        "collected_count": 383,
+        "comment_count": 63,
+        "shared_count": 14
+      },
+      "extraction": null,
+      "is_knowledge": true,
+      "knowledge_reason": "帖子标题明确标注“描改教程(细讲线稿)”,正文也有“教程”标签,图片展示了教程成品,清晰表明这是一个关于描改线稿的创作方法和技巧指导。",
+      "post_relevance_score": 0.45,
+      "relevance_level": "中度相关",
+      "relevance_reason": "该帖子提供了描改教程,包含创作方法,可以用于学习制作梗图。但它是一个通用的描改线稿教程,并未涉及如何将人类双标行为融入到猫咪表情包创作中,也未提供猫咪表情包相关的具体知识或案例,因此相关性中等偏低。"
+    },
+    "post_68429814000000002001dfc8_3_6": {
+      "type": "post",
+      "query": "[R] 还是梗图描改",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "68429814000000002001dfc8",
+      "note_url": "https://www.xiaohongshu.com/explore/68429814000000002001dfc8",
+      "body_text": "没错还是画梗图😁 原图后三张 受不了了画一个小时笑俩小时 含私/黑蓝/蓝卡其/卡其青cb #sprunki  #摸鱼",
+      "images": [
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831icn9qmcgek05pp5sknndqaieuvgs7g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831icn9qoag8ag5pp5sknndqaico0cqpo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831icn9qoag8905pp5sknndqai57tojo0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831icn9qoag89g5pp5sknndqaimkuc53g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831icn9qoag8a05pp5sknndqai9kf7v8o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831icn9qoag8cg5pp5sknndqaivg2f3lg?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831icn9qmcgek05pp5sknndqaieuvgs7g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831icn9qoag8ag5pp5sknndqaico0cqpo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831icn9qoag8905pp5sknndqai57tojo0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831icn9qoag89g5pp5sknndqaimkuc53g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831icn9qoag8a05pp5sknndqai9kf7v8o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831icn9qoag8cg5pp5sknndqaivg2f3lg?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 592,
+        "collected_count": 305,
+        "comment_count": 8,
+        "shared_count": 12
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "帖子是个人梗图描改分享,展示了作者将现有梗图素材进行二次创作的结果,但未提供任何可复用的创作方法、技巧或教程,不具备知识指导性。",
+      "post_relevance_score": 0.45,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子内容是梗图描改,与原始问题“如何制作反映人类双标行为的猫咪表情包梗图”有一定联系,因为它展示了梗图二创的成果,其中暗含了双标行为。但帖子缺乏具体的猫咪元素和创作方法指导,无法直接解决问题,参考价值有限。"
+    },
+    "post_68f485a60000000003010182_3_7": {
+      "type": "post",
+      "query": "[R] 神探妙探巴布洛梗图描改(1/)",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "68f485a60000000003010182",
+      "note_url": "https://www.xiaohongshu.com/explore/68f485a60000000003010182",
+      "body_text": "巴布洛:“我们非得这样坐吗?” 尼克:(摇尾巴) 依旧是@我想画稿 老师画的 #神犬妙探巴布洛  #同人产粮节  #巴布",
+      "images": [
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031nqfgvlo6m005pmc3nm2u2vnq2tut18?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031nqfgvlo6m005pmc3nm2u2vnq2tut18?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 68,
+        "collected_count": 9,
+        "comment_count": 10,
+        "shared_count": 1
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子是纯粹的同人作品分享和个人创作展示,属于娱乐内容。它没有提供任何关于表情包制作方法、技巧、工具使用或创作流程的知识。",
+      "post_relevance_score": 0.4,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子中描写的猫咪(巴布洛)体现了某种人类行为,具有梗图形式,与原始问题在概念上略有重叠。但它仅是一个成品而非制作教程,因此参考价值有限,不能解决原始问题。"
+    },
+    "post_682b5cc3000000000f031bf9_3_8": {
+      "type": "post",
+      "query": "[R] 描改手书教程",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "682b5cc3000000000f031bf9",
+      "note_url": "https://www.xiaohongshu.com/explore/682b5cc3000000000f031bf9",
+      "body_text": "戎星野是凑时间的#手书教程  ,创作不易,希望多点点赞#描改手书  #手书  #自信小孩  #鬼图打码  #meme",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g2sg31if41ea20c605q0op661o665pevjdf0?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31if41ea20c605q0op661o665pevjdf0?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 22394,
+        "collected_count": 13645,
+        "comment_count": 250,
+        "shared_count": 691
+      },
+      "extraction": null,
+      "is_knowledge": true,
+      "knowledge_reason": "标题明确指出是“描改手书教程”,图片内容也显示“教你怎么描改手书”,表明此帖提供了可学习、可复用的创作教学或指导。",
+      "post_relevance_score": 0.4,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子内容是关于“描改手书教程”,而原始问题是“如何制作反映人类双标行为的猫咪表情包梗图”。虽然都属于创作范畴,但描改手书的知识与梗图表情包的制作存在较大差异且不直接相关。描改手书可能提供一定的人物或动物形象创作技巧,但与猫咪表情包以及人类双标行为的主题关联度很低。"
+    },
+    "post_681c78d50000000020028a5c_3_9": {
+      "type": "post",
+      "query": "[R] go部分梗图描改",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "681c78d50000000020028a5c",
+      "note_url": "https://www.xiaohongshu.com/explore/681c78d50000000020028a5c",
+      "body_text": "#mygo  #avemujica  #丰川祥子  #千早爱音  #祐天寺若麦  #三角初华  #纯田真奈  #梗图",
+      "images": [
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031h7fu9mb3idg5od9e0s41ibh6cpuun0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031h7fu9q53ua05od9e0s41ibh1qmm0io?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031h7fu9q53u705od9e0s41ibhfcsj9d8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031h7fu9q53uc05od9e0s41ibhaajha1o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031h7fu9q53ucg5od9e0s41ibhp1g1120?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031h7fu9q53ud05od9e0s41ibhi8qlef0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031h7fu9q53udg5od9e0s41ibhf7oef0g?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031h7fu9mb3idg5od9e0s41ibh6cpuun0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031h7fu9q53ua05od9e0s41ibh1qmm0io?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031h7fu9q53u705od9e0s41ibhfcsj9d8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031h7fu9q53uc05od9e0s41ibhaajha1o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031h7fu9q53ucg5od9e0s41ibhp1g1120?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031h7fu9q53ud05od9e0s41ibhi8qlef0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031h7fu9q53udg5od9e0s41ibhf7oef0g?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 1118,
+        "collected_count": 166,
+        "comment_count": 32,
+        "shared_count": 121
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "帖子是用户分享的个人创作梗图作品,主要内容是利用动漫角色描改现有梗图,无关于创作方法、技巧或流程的指导,不包含可复用的创作知识。",
+      "post_relevance_score": 0.05,
+      "relevance_level": "低度相关",
+      "relevance_reason": "该帖子与原始问题“如何制作反映人类双标行为的猫咪表情包梗图”几乎不相关。帖子内容是关于动漫角色梗图描改,既没有涉及猫咪主题,也没有体现双标行为的制作方法或相关讨论,图片内容也与原始问题无关。因此参考价值极低。"
+    },
+    "search_梗图meme原创_r2_4": {
+      "type": "search_word",
+      "query": "[SEARCH] 梗图meme原创",
+      "level": 22,
+      "relevance_score": 0.654,
+      "strategy": "搜索词",
+      "iteration": 2,
+      "is_selected": true
+    },
+    "post_68e06587000000000401467d_4_0": {
+      "type": "post",
+      "query": "[R] 看香蕉meme🍌😲",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "68e06587000000000401467d",
+      "note_url": "https://www.xiaohongshu.com/explore/68e06587000000000401467d",
+      "body_text": "#meme  #国宴  #抽象  #猎奇  #梗图  #娱乐  #完美的精神状态",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g2sg31n6qgj75mgkg5pulgj039d3sjlcar4g?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31n6qgj75mgkg5pulgj039d3sjlcar4g?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 9047,
+        "collected_count": 2243,
+        "comment_count": 506,
+        "shared_count": 2145
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子为纯粹的娱乐分享和情感表达,内容没有提供任何关于创作表情包的方法、技巧、工具或流程等实用性知识。",
+      "post_relevance_score": 0.05,
+      "relevance_level": "低度相关",
+      "relevance_reason": "帖子内容是关于一个香蕉meme图,主题与原始问题“如何制作反映人类双标行为的猫咪表情包梗图”完全不符。它既没有涉及猫咪表情包,也未提及双标行为,更没有提供任何创作指导,因此相关性极低。"
+    },
+    "post_690a02720000000005002039_4_1": {
+      "type": "post",
+      "query": "[R] 野兽先生😧🤓😧",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "690a02720000000005002039",
+      "note_url": "https://www.xiaohongshu.com/explore/690a02720000000005002039",
+      "body_text": "#野兽先生  #meme  #抽象  #猎奇  #梗图  #国宴  #原创",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g2sg31off1cmdl8005pulgj039d3sqcl5l60?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31off1cmdl8005pulgj039d3sqcl5l60?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 7006,
+        "collected_count": 2883,
+        "comment_count": 243,
+        "shared_count": 455
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子仅包含一张图片和几个标签,未提供任何关于如何制作表情包或梗图的可复用创作方法、技巧、工具使用或教程指导。它只是一个内容的展示,并非知识分享。",
+      "post_relevance_score": 0.05,
+      "relevance_level": "低度相关",
+      "relevance_reason": "该帖子与原始问题“如何制作反映人类双标行为的猫咪表情包梗图”几乎不相关。帖子内容是关于“野兽先生”的YouTube视频截图,与猫咪、双标行为或表情包制作方法都无关,也缺乏任何创作指导。"
+    },
+    "post_68b27225000000001b03e3a4_4_2": {
+      "type": "post",
+      "query": "[R] 老奶奶打方向盘meme",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "68b27225000000001b03e3a4",
+      "note_url": "https://www.xiaohongshu.com/explore/68b27225000000001b03e3a4",
+      "body_text": "#老奶奶打方向盘  #memes  #原创  #搞笑  #娱乐  #梗图  #禁止模仿搬运",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g2sg31lpuj0fvlu605pulgj039d3s3cchlt0?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31lpuj0fvlu605pulgj039d3s3cchlt0?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 24890,
+        "collected_count": 5919,
+        "comment_count": 914,
+        "shared_count": 2531
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "帖子的标题、正文和图片直接展示的是一个具体的梗图,而非关于如何制作梗图的创作方法、技巧或教程指导。它是一个成品展示,不包含可复用的创作知识。",
+      "post_relevance_score": 0.1,
+      "relevance_level": "低度相关",
+      "relevance_reason": "该帖子为一个具体的时事梗(老奶奶打方向盘meme)展示,与如何制作反映人类双标行为的猫咪表情包梗图的原始问题关联性微弱。帖子展示的梗图与猫咪、双标行为均不相关,也未提供任何制作方法或工具信息,因此参考价值极低。"
+    },
+    "post_677e6b540000000001000781_4_3": {
+      "type": "post",
+      "query": "[R] 请选择你要使用的可爱",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "677e6b540000000001000781",
+      "note_url": "https://www.xiaohongshu.com/explore/677e6b540000000001000781",
+      "body_text": "#表情包  #猎奇 #抽象  #meme  #原创  #插画  #养成系",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g00831cd4r0ke0s005ngkmrm0899i9usp0ro?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831cd4r0ke0s0g5ngkmrm0899il23816o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831cd4r0ke0s105ngkmrm0899i1jk4djg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831cd4r0ke0s1g5ngkmrm0899i3l4g08g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831cd4r0ke0s205ngkmrm0899iv3rutig?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831cd4r0ke0s2g5ngkmrm0899idq09amo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831cd4r0ke0s305ngkmrm0899ivilp1m0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831cd4r0ke0s3g5ngkmrm0899i1bkdj3o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831cd4r0ke0s405ngkmrm0899i1gcubqo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831cd4r0ke0s4g5ngkmrm0899im2rrckg?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831cd4r0ke0s005ngkmrm0899i9usp0ro?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831cd4r0ke0s0g5ngkmrm0899il23816o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831cd4r0ke0s105ngkmrm0899i1jk4djg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831cd4r0ke0s1g5ngkmrm0899i3l4g08g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831cd4r0ke0s205ngkmrm0899iv3rutig?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831cd4r0ke0s2g5ngkmrm0899idq09amo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831cd4r0ke0s305ngkmrm0899ivilp1m0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831cd4r0ke0s3g5ngkmrm0899i1bkdj3o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831cd4r0ke0s405ngkmrm0899i1gcubqo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831cd4r0ke0s4g5ngkmrm0899im2rrckg?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 1517,
+        "collected_count": 311,
+        "comment_count": 259,
+        "shared_count": 440
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子为表情包展示,无任何关于表情包制作方法、技巧、工具使用或流程指导等可复用的创作知识内容。",
+      "post_relevance_score": 0.2,
+      "relevance_level": "低度相关",
+      "relevance_reason": "帖子内容是展示已完成的表情包,与原始问题“如何制作反映人类双标行为的猫咪表情包梗图”相关性很弱。它不提供任何制作教程或思路,只是提供了参考表情包样式,且表情包并非猫咪形象,不含双标行为的体现。"
+    },
+    "post_6640beca000000001e023a10_4_4": {
+      "type": "post",
+      "query": "[R] meme梗图:这是我设计的怪物",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "6640beca000000001e023a10",
+      "note_url": "https://www.xiaohongshu.com/explore/6640beca000000001e023a10",
+      "body_text": "💫 一个发无水印#meme  的账号 🕚 日更【译制】or【原创】meme梗图笔记#梗图  #沙雕搞笑  #幽默搞笑",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g008312mth2u3ie6g5p8mibd0hkqg93s06r0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008312mth2u3ie605p8mibd0hkqgbvmfkco?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008312mth2u3ie5g5p8mibd0hkqgljkoho0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008312mth2u3ie505p8mibd0hkqgbc1mtso?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008312mth2u3ie4g5p8mibd0hkqg5ndqh8o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008312mth2u3ie105p8mibd0hkqgjuigjm0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008312mth2u3ie405p8mibd0hkqgvvei808?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008312mth2u3ie3g5p8mibd0hkqgmm4t19g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008312mth2u3ie305p8mibd0hkqgp2ds1tg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008312mth2u3ie1g5p8mibd0hkqggv5p3e8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008312mth2u3ie205p8mibd0hkqg7svhqlg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008312mth2u3ie0g5p8mibd0hkqgg14kero?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008312mth2u3ie005p8mibd0hkqgb53sna0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008312mth2u3ie2g5p8mibd0hkqgsge9r70?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008312muov1a2cd05p8mibd0hkqghrm9o58?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008312mth2u3ie6g5p8mibd0hkqg93s06r0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008312mth2u3ie605p8mibd0hkqgbvmfkco?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008312mth2u3ie5g5p8mibd0hkqgljkoho0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008312mth2u3ie505p8mibd0hkqgbc1mtso?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008312mth2u3ie4g5p8mibd0hkqg5ndqh8o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008312mth2u3ie105p8mibd0hkqgjuigjm0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008312mth2u3ie405p8mibd0hkqgvvei808?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008312mth2u3ie3g5p8mibd0hkqgmm4t19g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008312mth2u3ie305p8mibd0hkqgp2ds1tg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008312mth2u3ie1g5p8mibd0hkqggv5p3e8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008312mth2u3ie205p8mibd0hkqg7svhqlg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008312mth2u3ie0g5p8mibd0hkqgg14kero?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008312mth2u3ie005p8mibd0hkqgb53sna0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008312mth2u3ie2g5p8mibd0hkqgsge9r70?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008312muov1a2cd05p8mibd0hkqghrm9o58?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 3917,
+        "collected_count": 434,
+        "comment_count": 66,
+        "shared_count": 73
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子主要是一个meme图的分享账号,内容是已制作好的搞笑梗图合集,不包含可学习、可复用的创作方法、技巧或教程指导,不属于面向创作的知识内容。",
+      "post_relevance_score": 0.2,
+      "relevance_level": "低度相关",
+      "relevance_reason": "帖子中虽然部分梗图涉及动物(猫、猴、狗)和幽默内容,但与原始问题“如何制作反映人类双标行为的猫咪表情包梗图”的核心诉求“制作方法”和“双标行为”无关。它仅提供了现成的meme图,部分图片中出现猫,但没有聚焦于“双标行为”的表达,也未提供任何制作指导。"
+    },
+    "post_67a38120000000001801354d_4_5": {
+      "type": "post",
+      "query": "[R] 自己原创了几个meme梗图",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "67a38120000000001801354d",
+      "note_url": "https://www.xiaohongshu.com/explore/67a38120000000001801354d",
+      "body_text": "发现自制梗图真的是件很有趣的事 所有的meme图均为原创[害羞R]#梗图  #meme  #有趣日常  #原创梗图",
+      "images": [
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831dhbm82pgk705nff308g8011pgdv1ro?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831dhbm82pgk7g5nff308g8011pd4or78?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831dhbm82pgk805nff308g8011eko036g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831dhbm82pgk8g5nff308g80117lqdhv0?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831dhbm82pgk705nff308g8011pgdv1ro?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831dhbm82pgk7g5nff308g8011pd4or78?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831dhbm82pgk805nff308g8011eko036g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831dhbm82pgk8g5nff308g80117lqdhv0?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 16,
+        "collected_count": 3,
+        "comment_count": 1,
+        "shared_count": 3
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "帖子正文仅是个人分享制作梗图的乐趣,没有提供任何关于如何制作梗图的方法、技巧、工具使用或具体流程指导,不包含可复用的创作知识。",
+      "post_relevance_score": 0.05,
+      "relevance_level": "低度相关",
+      "relevance_reason": "帖子内容与原始问题“如何制作反映人类双标行为的猫咪表情包梗图”几乎不相关。帖子只是分享了原创梗图,但未涉及梗图的制作方法,更没有提及猫咪表情包或人类双标行为的主题,提供不了任何解决原始问题所需的知识或方法。"
+    },
+    "post_66f6d0df000000002c029dc7_4_6": {
+      "type": "post",
+      "query": "[R] 大A,我来了 | meme梗图①",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "66f6d0df000000002c029dc7",
+      "note_url": "https://www.xiaohongshu.com/explore/66f6d0df000000002c029dc7",
+      "body_text": "只用了3天, sz指数就从2700点站到了3000点上, 不是做梦,不是做梦,不是做梦 今天是2024年9月27日 让我",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g0083188n7gqt3q0049si7hnl20uo9m0mmt8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g0083188n7gqt3q0g49si7hnl20uon6h7a5o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g0083188n7gqt3q1049si7hnl20uo7g6bsoo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g0083188n7gqt3q2049si7hnl20uotlb7n6g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g0083188n7gqt3q2g49si7hnl20uoe6oc860?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g0083188n7gqt3q3049si7hnl20uocb57cl8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g0083188n7gqt3q3g49si7hnl20uopdtkvvg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g0083188n7gqt3q4049si7hnl20uo90437oo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g0083188n7gqt3q4g49si7hnl20uo4kok46o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g0083188ob4u5kc0049si7hnl20uoh0vr6do?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g0083188n7gqt3q5049si7hnl20uo52587go?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g0083188n7gqt3q5g49si7hnl20uo5bbt10o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g0083188n7gqt3q6049si7hnl20uousf8r7g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g0083188n7gqt3q6g49si7hnl20uob0a4ko8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g0083188n7gs4jq0049si7hnl20uo3u9qig0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g0083188n7gs53q0049si7hnl20uoesuvn9o?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g0083188n7gqt3q0049si7hnl20uo9m0mmt8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g0083188n7gqt3q0g49si7hnl20uon6h7a5o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g0083188n7gqt3q1049si7hnl20uo7g6bsoo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g0083188n7gqt3q2049si7hnl20uotlb7n6g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g0083188n7gqt3q2g49si7hnl20uoe6oc860?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g0083188n7gqt3q3049si7hnl20uocb57cl8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g0083188n7gqt3q3g49si7hnl20uopdtkvvg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g0083188n7gqt3q4049si7hnl20uo90437oo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g0083188n7gqt3q4g49si7hnl20uo4kok46o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g0083188ob4u5kc0049si7hnl20uoh0vr6do?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g0083188n7gqt3q5049si7hnl20uo52587go?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g0083188n7gqt3q5g49si7hnl20uo5bbt10o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g0083188n7gqt3q6049si7hnl20uousf8r7g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g0083188n7gqt3q6g49si7hnl20uob0a4ko8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g0083188n7gs4jq0049si7hnl20uo3u9qig0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g0083188n7gs53q0049si7hnl20uoesuvn9o?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 110,
+        "collected_count": 46,
+        "comment_count": 6,
+        "shared_count": 35
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子为对近期股市行情的情感表达和个人感受的分享,配图多为网络梗图或表情包,不包含任何关于创作表情包梗图的方法、技巧或工具使用等可复用的知识内容。",
+      "post_relevance_score": 0.1,
+      "relevance_level": "低度相关",
+      "relevance_reason": "帖子内容本身是关于中国股市(大A)的meme梗图合集,与原始问题“如何制作反映人类双标行为的猫咪表情包梗图”相关性极低。帖子没有提供任何制作猫咪表情包或反映双标行为的知识,仅展示了与股市情绪相关的梗图。"
+    },
+    "post_686cfdf60000000023005633_4_7": {
+      "type": "post",
+      "query": "[R] 失离",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "686cfdf60000000023005633",
+      "note_url": "https://www.xiaohongshu.com/explore/686cfdf60000000023005633",
+      "body_text": "黑暗meme梗图 搬运 2. 答案 ? #梗图  #创人  #原创未知  #猎奇  #黑暗meme #迷因 #meme",
+      "images": [
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031jm47rebio505ps8cv41o5gdngn1kbg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831jm44o24j27g5ps8cv41o5gdmeahhug?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831jm44o24j2a05ps8cv41o5gd0hsjra8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831jm44o24j2805ps8cv41o5gdtnb8a30?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031jm47red34005ps8cv41o5gdl2l5ip0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31jnve2ro2ofg5ps8cv41o5gdfod1ih8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31jnve2ro2ogg5ps8cv41o5gdmir5198?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31jnve2ro2ojg5ps8cv41o5gdq1s7si8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31jnve2ro2oi05ps8cv41o5gdg8mc3o0?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031jm47rebio505ps8cv41o5gdngn1kbg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831jm44o24j27g5ps8cv41o5gdmeahhug?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831jm44o24j2a05ps8cv41o5gd0hsjra8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831jm44o24j2805ps8cv41o5gdtnb8a30?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031jm47red34005ps8cv41o5gdl2l5ip0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31jnve2ro2ofg5ps8cv41o5gdfod1ih8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31jnve2ro2ogg5ps8cv41o5gdmir5198?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31jnve2ro2ojg5ps8cv41o5gdq1s7si8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31jnve2ro2oi05ps8cv41o5gdg8mc3o0?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 1969,
+        "collected_count": 651,
+        "comment_count": 20,
+        "shared_count": 98
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子主要由一系列猎奇、黑暗风格的梗图组成,内容为个人搬运和分享,属于纯粹的娱乐性内容,不包含任何关于表情包创作的方法、技巧、工具或教程指导等实用知识。",
+      "post_relevance_score": 0.05,
+      "relevance_level": "低度相关",
+      "relevance_reason": "该帖子与原始问题“如何制作反映人类双标行为的猫咪表情包梗图”几乎没有相关性。帖子内容是黑暗meme的搬运,主题与猫咪、双标行为以及表情包制作都无关,完全无法提供解决问题所需的知识或方法。"
+    },
+    "post_674db30c00000000070278f1_4_8": {
+      "type": "post",
+      "query": "[R] 自制meme Man梗图",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "674db30c00000000070278f1",
+      "note_url": "https://www.xiaohongshu.com/explore/674db30c00000000070278f1",
+      "body_text": "#meme #meme梗图  #memeMan  #抽象",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g00831atib2me6u5g5p3ds4ejour91g23j6o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831atib2me6u605p3ds4ejour93fc3suo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831atib2me6u505p3ds4ejour90ce8alg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831atib2me6u3g5p3ds4ejour9nfgmer0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831atib2me6u305p3ds4ejour9s0q6gv0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831atib2me6u405p3ds4ejour90dtggjg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831atib2me6u205p3ds4ejour9d1j0vng?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831atib2me6u2g5p3ds4ejour90980cf8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831atib2me6u1g5p3ds4ejour96alt3co?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831atib2me6u105p3ds4ejour9rr4vp3o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831atib2me6u4g5p3ds4ejour9teunjm8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831atib2me6u005p3ds4ejour9ujd5hc8?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831atib2me6u5g5p3ds4ejour91g23j6o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831atib2me6u605p3ds4ejour93fc3suo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831atib2me6u505p3ds4ejour90ce8alg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831atib2me6u3g5p3ds4ejour9nfgmer0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831atib2me6u305p3ds4ejour9s0q6gv0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831atib2me6u405p3ds4ejour90dtggjg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831atib2me6u205p3ds4ejour9d1j0vng?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831atib2me6u2g5p3ds4ejour90980cf8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831atib2me6u1g5p3ds4ejour96alt3co?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831atib2me6u105p3ds4ejour9rr4vp3o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831atib2me6u4g5p3ds4ejour9teunjm8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831atib2me6u005p3ds4ejour9ujd5hc8?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 1002,
+        "collected_count": 420,
+        "comment_count": 24,
+        "shared_count": 99
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子仅分享了Stonks Meme梗图成品,未提供任何关于如何制作Stonks Meme、如何将内容与双标行为结合或使用猫咪形象的创作方法、工具或教程。",
+      "post_relevance_score": 0.1,
+      "relevance_level": "低度相关",
+      "relevance_reason": "帖子内容是Stonks Meme系列梗图,与原始问题的'猫咪表情包梗图'不符,也没有涉及'反映人类双标行为'的主题。虽然都是梗图,但其风格、形象和表达内容与原始问题差异巨大,无法提供有效参考。"
+    },
+    "post_6913214e0000000003034f7f_4_9": {
+      "type": "post",
+      "query": "[R] 没有不可爱的义务!(`へ´*)ノ",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "6913214e0000000003034f7f",
+      "note_url": "https://www.xiaohongshu.com/explore/6913214e0000000003034f7f",
+      "body_text": "窝就这样!能拿窝咋地👉👈~~~ #原创ip  #ip  #梗图  #原创IP  #表情包  #可爱  #meme",
+      "images": [
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031ooc06146g005pm5n55iu73ep2p0ul8?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031ooc06146g005pm5n55iu73ep2p0ul8?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 38,
+        "collected_count": 9,
+        "comment_count": 5,
+        "shared_count": 1
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子为纯个人原创IP的展示和情感表达,不包含任何关于表情包制作的方法、技巧、工具使用或流程步骤等可复用的创作知识。",
+      "post_relevance_score": 0.05,
+      "relevance_level": "低度相关",
+      "relevance_reason": "原始问题是关于如何制作反映人类双标行为的猫咪表情包梗图。该帖子虽然涉及“梗图”和“表情包”的标签,但内容并未提及猫咪,也未涉及如何反映双标行为,更没有任何制作方法。因此,其与原始问题的相关性极低,无法提供任何有效参考。"
+    },
+    "search_猫咪表情包梗图搞笑_r2_5": {
+      "type": "search_word",
+      "query": "[SEARCH] 猫咪表情包梗图搞笑",
+      "level": 22,
+      "relevance_score": 0.6000000000000001,
+      "strategy": "搜索词",
+      "iteration": 2,
+      "is_selected": true
+    },
+    "post_67d2310a000000001d026c02_5_0": {
+      "type": "post",
+      "query": "[R] 抽象猫猫表情包",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "67d2310a000000001d026c02",
+      "note_url": "https://www.xiaohongshu.com/explore/67d2310a000000001d026c02",
+      "body_text": "#猫咪表情包  #如果猫猫会说话  #每日表情包分享",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g2sg31euuncaglu6g5pbpmdto6fqbk8t7v90?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31euuncgcmqdg5pbpmdto6fqbbg8l43o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831euuncgdmm605pbpmdto6fqb386sn00?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31euuncgbludg5pbpmdto6fqbfpihpd8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31euuncf15ud05pbpmdto6fqbh03riso?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831euuncf15s605pbpmdto6fqbnfufsio?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31euunccblu6g5pbpmdto6fqbct48sdg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831euunc9r6s405pbpmdto6fqbte9172g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31euuncah5scg5pbpmdto6fqb0refke0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831euunc9rls6g5pbpmdto6fqbb5a40c8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831euunc9o6m6g5pbpmdto6fqb8pi31ao?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831euuncahlu5g5pbpmdto6fqb6js6ih0?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31euuncaglu6g5pbpmdto6fqbk8t7v90?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31euuncgcmqdg5pbpmdto6fqbbg8l43o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831euuncgdmm605pbpmdto6fqb386sn00?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31euuncgbludg5pbpmdto6fqbfpihpd8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31euuncf15ud05pbpmdto6fqbh03riso?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831euuncf15s605pbpmdto6fqbnfufsio?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31euunccblu6g5pbpmdto6fqbct48sdg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831euunc9r6s405pbpmdto6fqbte9172g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31euuncah5scg5pbpmdto6fqb0refke0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831euunc9rls6g5pbpmdto6fqbb5a40c8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831euunc9o6m6g5pbpmdto6fqb8pi31ao?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831euuncahlu5g5pbpmdto6fqb6js6ih0?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 4281,
+        "collected_count": 924,
+        "comment_count": 143,
+        "shared_count": 239
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子仅分享了一组猫咪表情包图片,标题和正文也只是简单的标签和分享性文字。不包含任何关于表情包制作方法、技巧、工具使用或教程指导等可复用的创作知识。",
+      "post_relevance_score": 0.4,
+      "relevance_level": "中度相关",
+      "relevance_reason": "该帖子提供了猫咪表情包成品,可以作为制作灵感参考,但未提供如何制作“反映人类双标行为”的表情包的具体方法或指导,因此相关性较低,仅提供部分视觉灵感。"
+    },
+    "post_68d8bddf000000000e031155_5_1": {
+      "type": "post",
+      "query": "[R] 就爱收藏阿猫阿狗抽象表情包",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "68d8bddf000000000e031155",
+      "note_url": "https://www.xiaohongshu.com/explore/68d8bddf000000000e031155",
+      "body_text": "#宠物表情包  #定格有梗瞬间  #捕捉动物的表情包  #有趣的表情包  #猫咪表情包  #干翻这个世界  #每日表情包",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g00831mvb43s06m4g5nrvsp8g91o5dfl2sgg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831mvb43s06m305nrvsp8g91o5kks4c78?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831mvb43s06m3g5nrvsp8g91o5f6ltin8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831mvb43s06m005nrvsp8g91o5sg0m5u8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831mvb43s06m6g5nrvsp8g91o5lbf2pc0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831mvbdgijmg005nrvsp8g91o5psiunh8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831mvb43s06m605nrvsp8g91o5ujanhlg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831mvb43s06m0g5nrvsp8g91o5qe7k490?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831mvb43s06m105nrvsp8g91o51k0bpng?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831mvb43s06m1g5nrvsp8g91o585spb60?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831mvb43s06m405nrvsp8g91o5lf5j0i8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831mvbdgijmg105nrvsp8g91o5q7emmqo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831mvb43s06m205nrvsp8g91o5gvkvolg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831mvb43s06m2g5nrvsp8g91o5c82q7ao?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831mvbdgijmg0g5nrvsp8g91o57qegvf8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831mvb43s06m5g5nrvsp8g91o57i9jt10?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831mvb43s06m505nrvsp8g91o5o6tgc08?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831mvb43s06m4g5nrvsp8g91o5dfl2sgg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831mvb43s06m305nrvsp8g91o5kks4c78?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831mvb43s06m3g5nrvsp8g91o5f6ltin8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831mvb43s06m005nrvsp8g91o5sg0m5u8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831mvb43s06m6g5nrvsp8g91o5lbf2pc0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831mvbdgijmg005nrvsp8g91o5psiunh8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831mvb43s06m605nrvsp8g91o5ujanhlg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831mvb43s06m0g5nrvsp8g91o5qe7k490?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831mvb43s06m105nrvsp8g91o51k0bpng?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831mvb43s06m1g5nrvsp8g91o585spb60?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831mvb43s06m405nrvsp8g91o5lf5j0i8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831mvbdgijmg105nrvsp8g91o5q7emmqo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831mvb43s06m205nrvsp8g91o5gvkvolg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831mvb43s06m2g5nrvsp8g91o5c82q7ao?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831mvbdgijmg0g5nrvsp8g91o57qegvf8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831mvb43s06m5g5nrvsp8g91o57i9jt10?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831mvb43s06m505nrvsp8g91o5o6tgc08?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 2461,
+        "collected_count": 673,
+        "comment_count": 64,
+        "shared_count": 566
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子为纯粹的表情包分享,内容旨在娱乐和情感表达,不包含任何关于表情包制作的方法、技巧、工具使用或教程指导等可复用的创作知识。",
+      "post_relevance_score": 0.4,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子内容展示了一些猫咪表情包,其中部分图片能隐约体现出双标或拟人化的幽默情境,例如两只猫排泄的图片可能略微暗示双标。但帖子未提供任何关于如何制作或创作这类表情包的知识,其参考价值非常有限,未能直接解决原始问题。"
+    },
+    "post_6710c5a7000000001402dc9f_5_2": {
+      "type": "post",
+      "query": "[R] 太好了是小猫表情包,我们有救了!",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "6710c5a7000000001402dc9f",
+      "note_url": "https://www.xiaohongshu.com/explore/6710c5a7000000001402dc9f",
+      "body_text": "丫头们久等了,哥的第十一弹表情包来了[kissR][kissR][kissR] #我的工具宠  #解闷宠  #田园猫",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g2sg31922bs335a704a541utvp5rcl5mfueo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31922bs335a7g4a541utvp5rcovup6io?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31922bs335a804a541utvp5rcgc5as3g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31922bs335a8g4a541utvp5rcsithfdo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31922bs335a904a541utvp5rca4nsvh0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31922bs335a9g4a541utvp5rc28ll0eo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31922bs335aa04a541utvp5rca58js3g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31922bs335aag4a541utvp5rcdrhk04g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31922bs335ab04a541utvp5rcddn19ag?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31922bs335abg4a541utvp5rc7d6r16o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31922bs335ac04a541utvp5rcq0msb40?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31922bs335acg4a541utvp5rc2s5j7v8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31922bs335ad04a541utvp5rc03dtr28?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31922bs335adg4a541utvp5rc8fk781o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31922bs4058704a541utvp5rc9q3odro?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31922bs40587g4a541utvp5rc6gk218g?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31922bs335a704a541utvp5rcl5mfueo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31922bs335a7g4a541utvp5rcovup6io?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31922bs335a804a541utvp5rcgc5as3g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31922bs335a8g4a541utvp5rcsithfdo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31922bs335a904a541utvp5rca4nsvh0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31922bs335a9g4a541utvp5rc28ll0eo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31922bs335aa04a541utvp5rca58js3g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31922bs335aag4a541utvp5rcdrhk04g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31922bs335ab04a541utvp5rcddn19ag?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31922bs335abg4a541utvp5rc7d6r16o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31922bs335ac04a541utvp5rcq0msb40?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31922bs335acg4a541utvp5rc2s5j7v8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31922bs335ad04a541utvp5rc03dtr28?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31922bs335adg4a541utvp5rc8fk781o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31922bs4058704a541utvp5rc9q3odro?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31922bs40587g4a541utvp5rc6gk218g?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 9406,
+        "collected_count": 2019,
+        "comment_count": 335,
+        "shared_count": 1019
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子仅分享了猫咪表情包成品,未包含任何关于如何制作表情包的方法、技巧、工具使用、流程步骤或教程指导等创作知识。",
+      "post_relevance_score": 0.2,
+      "relevance_level": "低度相关",
+      "relevance_reason": "帖子内容是成品表情包的分享,虽然与“猫咪表情包梗图”这一主题相关,但没有提供任何关于“如何制作”或“反映人类双标行为”的创作知识或方法,因此对解决原始问题帮助不大,相关性较低。"
+    },
+    "post_6731806d000000001b02cf7f_5_3": {
+      "type": "post",
+      "query": "[R] 小猫干架表情包合辑2",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "6731806d000000001b02cf7f",
+      "note_url": "https://www.xiaohongshu.com/explore/6731806d000000001b02cf7f",
+      "body_text": "#猫咪性格  #可可爱爱表情包  #猫咪表情包  #表情包  #猫咪日常  #宠物表情包  #虞书欣凌妙妙  #猫咪",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g00831a1uuq9b72605niem91g840b1fm2hvg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831a1uuq9b721g5niem91g840birajg18?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831a1uuq9b72405niem91g840bggddgqo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831a1uuq9b72105niem91g840b6o0okeo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31a215e2572dg5niem91g840bmmms1ng?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31a215e2572d05niem91g840bh7mcg98?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31a215e2572bg5niem91g840bj137cv8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31a215e2572b05niem91g840b6lehi6g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31a215e25729g5niem91g840bsfg5fvg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31a215e25728g5niem91g840bin01a0o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31a215e2572cg5niem91g840bqkmp4qg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31a215e2572a05niem91g840bp8u9cr0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31a215e2572805niem91g840bbmm1hko?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31a215e2572705niem91g840b8ku2e38?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31a215e2572905niem91g840b1u5cps8?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831a1uuq9b72605niem91g840b1fm2hvg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831a1uuq9b721g5niem91g840birajg18?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831a1uuq9b72405niem91g840bggddgqo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831a1uuq9b72105niem91g840b6o0okeo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31a215e2572dg5niem91g840bmmms1ng?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31a215e2572d05niem91g840bh7mcg98?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31a215e2572bg5niem91g840bj137cv8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31a215e2572b05niem91g840b6lehi6g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31a215e25729g5niem91g840bsfg5fvg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31a215e25728g5niem91g840bin01a0o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31a215e2572cg5niem91g840bqkmp4qg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31a215e2572a05niem91g840bp8u9cr0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31a215e2572805niem91g840bbmm1hko?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31a215e2572705niem91g840b8ku2e38?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31a215e2572905niem91g840b1u5cps8?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 2307,
+        "collected_count": 936,
+        "comment_count": 181,
+        "shared_count": 990
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "帖子仅提供了猫咪表情包合集,属于纯内容分享和情感表达,不包含任何可复用的创作方法、技巧或流程指导。",
+      "post_relevance_score": 0.4,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子提供了大量猫咪表情包,其中部分图片通过文案表达了攻击性、威胁等“双标”情绪。虽然没有直接指导创作,但这些已制作好的表情包可以作为创作素材或启发,为用户在构建“反映人类双标行为”的猫咪表情包时提供灵感,具有一定的间接参考价值。"
+    },
+    "post_66505e0c000000001401add3_5_4": {
+      "type": "post",
+      "query": "[R] 奥德彪经典语录 精神状态遥遥领先",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "66505e0c000000001401add3",
+      "note_url": "https://www.xiaohongshu.com/explore/66505e0c000000001401add3",
+      "body_text": "#奥德彪  #人间清醒  #发疯文学 #精神状态 #情感语录 #三观 #梗图  #笑话  #沙雕搞笑  #表情包  #精",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g2sg31365n37mnieg4a77u6v5i4tqkb632s8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31365n37mnie04a77u6v5i4tq7dkmk78?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31365n37mnif04a77u6v5i4tqn6o95mg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31365n37mnifg4a77u6v5i4tqc3oq9s8?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31365n37mnieg4a77u6v5i4tqkb632s8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31365n37mnie04a77u6v5i4tq7dkmk78?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31365n37mnif04a77u6v5i4tqn6o95mg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31365n37mnifg4a77u6v5i4tqc3oq9s8?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 33354,
+        "collected_count": 9411,
+        "comment_count": 1744,
+        "shared_count": 14441
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "帖子是纯粹的搞笑梗图分享,不包含任何关于如何制作表情包梗图的方法、技巧或教程指导,不具备可复用的创作知识。",
+      "post_relevance_score": 0.5,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子内容是猫咪表情包梗图,与原始问题的“猫咪表情包梗图”这一载体是匹配的。且图片中的确有所谓的“双标”或“人性的弱点”的表达,但是并未直接解决如何利用猫咪表现人类双标行为这一创作难点,整体内容是比较通用的段子,并非为了解决具体问题而设计,因此相关性为中等。"
+    },
+    "post_6629f62a00000000030233d9_5_5": {
+      "type": "post",
+      "query": "[R] 到底是谁还在用这么抽象的表情包啊",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "6629f62a00000000030233d9",
+      "note_url": "https://www.xiaohongshu.com/explore/6629f62a00000000030233d9",
+      "body_text": "#表情包分享 #记录猫咪日常  #萌宠出道计划  #每日表情包分享  #搞笑日常",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g2sg3120lhc7g3a705n414vv446ja1upig88?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg3120lhc7g3a7g5n414vv446jan9nfpn0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg3120lhc7g3a805n414vv446jaoj6r3g0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg3120lhc7g3a8g5n414vv446ja7r8ase8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg3120lhc7g3a905n414vv446jac7pj1bo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg3120lhc7g3a9g5n414vv446ja2nh59mg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg3120lhc7g3aa05n414vv446ja7fta530?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg3120lhc7g3aag5n414vv446jan52id0o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg3120lhc7g3ab05n414vv446javca1c4o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg3120lhc7g3abg5n414vv446ja11e0bg8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg3120lhc7g3ac05n414vv446jaueuipd8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg3120lhc7g3acg5n414vv446jalf0pgm8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg3120lhc7g3ad05n414vv446jal454m4g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg3120lhc7g3adg5n414vv446jad7g7vm8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g0083120lhc953c005n414vv446japjgnktg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g0083120lhc953c0g5n414vv446jatikjlj0?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3120lhc7g3a705n414vv446ja1upig88?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3120lhc7g3a7g5n414vv446jan9nfpn0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3120lhc7g3a805n414vv446jaoj6r3g0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3120lhc7g3a8g5n414vv446ja7r8ase8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3120lhc7g3a905n414vv446jac7pj1bo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3120lhc7g3a9g5n414vv446ja2nh59mg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3120lhc7g3aa05n414vv446ja7fta530?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3120lhc7g3aag5n414vv446jan52id0o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3120lhc7g3ab05n414vv446javca1c4o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3120lhc7g3abg5n414vv446ja11e0bg8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3120lhc7g3ac05n414vv446jaueuipd8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3120lhc7g3acg5n414vv446jalf0pgm8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3120lhc7g3ad05n414vv446jal454m4g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3120lhc7g3adg5n414vv446jad7g7vm8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g0083120lhc953c005n414vv446japjgnktg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g0083120lhc953c0g5n414vv446jatikjlj0?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 785,
+        "collected_count": 295,
+        "comment_count": 25,
+        "shared_count": 92
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "帖子是纯粹的表情包分享,未提供任何关于如何制作表情包的创作方法、技巧或教程指导。",
+      "post_relevance_score": 0.4,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子展示了一些猫咪表情包,其中部分(如“就凭你?”、“我不要”、“我想想”)可能间接反映出双标行为的某种情绪或场景,但未直接或深入探讨如何创作此类内容,也未提供具体制作方法。"
+    },
+    "post_68e71e340000000007002e56_5_6": {
+      "type": "post",
+      "query": "[R] 叛逆小猫在线打人",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "68e71e340000000007002e56",
+      "note_url": "https://www.xiaohongshu.com/explore/68e71e340000000007002e56",
+      "body_text": "表情包分享[棒R][棒R]#日常文案  #聊天  #表情包  #实用表情包  #每日表情包分享  #可爱表情包  #搞笑",
+      "images": [
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031ndcljtll0005pehqv8h3kd3n2ijvk0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031ndclkeq52jg5pehqv8h3kd35gksog8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031ndclkeq52eg5pehqv8h3kd3npk95fg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031ndclkeq52h05pehqv8h3kd31dc5cfo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031ndclkeq52f05pehqv8h3kd3g0sr448?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031ndclkeq52ig5pehqv8h3kd3a79bfsg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031ndclkeq52j05pehqv8h3kd3ed7pkno?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031ndcljtll0005pehqv8h3kd3n2ijvk0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031ndclkeq52jg5pehqv8h3kd35gksog8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031ndclkeq52eg5pehqv8h3kd3npk95fg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031ndclkeq52h05pehqv8h3kd31dc5cfo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031ndclkeq52f05pehqv8h3kd3g0sr448?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031ndclkeq52ig5pehqv8h3kd3a79bfsg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031ndclkeq52j05pehqv8h3kd3ed7pkno?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 72,
+        "collected_count": 18,
+        "comment_count": 2,
+        "shared_count": 13
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子仅为猫咪表情包的分享,不含任何可复用的创作方法、技巧、工具使用、流程步骤或教程指导,属于纯粹的个人内容分享和情感表达。",
+      "post_relevance_score": 0.4,
+      "relevance_level": "中度相关",
+      "relevance_reason": "该帖子提供了猫咪表情包梗图的成品,但未涉及如何“制作反映人类双标行为”的指导,也没有提供创作思路、工具教程或相关方法。虽然是猫咪表情包,但无法指导用户进行创作,仅有成品参考价值。"
+    },
+    "post_67cbe638000000001203e839_5_7": {
+      "type": "post",
+      "query": "[R] 猫meme表情包合集丨抽象~",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "67cbe638000000001203e839",
+      "note_url": "https://www.xiaohongshu.com/explore/67cbe638000000001203e839",
+      "body_text": "#表情包演我精神状态  #热门表情包  #趣味表情包  #发疯表情包  #猫猫的表情都写在脸上",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g2sg31eoq33og6edg5nini3cg8o3nvm1e94g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831eoq33rv666g5nini3cg8o3n1rhgirg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31eoq33rsmad05nini3cg8o3n0ct439o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31eoq33rmm6dg5nini3cg8o3nlc2ch10?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831eoq33q5mi6g5nini3cg8o3n3gvqc7o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831eoq33oem66g5nini3cg8o3n1u445no?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31eoq33ofm6d05nini3cg8o3naq3a7lo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831eoq33oemecg5nini3cg8o3n16r2jog?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831eoq33of6i605nini3cg8o3n35ps69g?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31eoq33og6edg5nini3cg8o3nvm1e94g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831eoq33rv666g5nini3cg8o3n1rhgirg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31eoq33rsmad05nini3cg8o3n0ct439o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31eoq33rmm6dg5nini3cg8o3nlc2ch10?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831eoq33q5mi6g5nini3cg8o3n3gvqc7o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831eoq33oem66g5nini3cg8o3n1u445no?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31eoq33ofm6d05nini3cg8o3naq3a7lo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831eoq33oemecg5nini3cg8o3n16r2jog?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831eoq33of6i605nini3cg8o3n35ps69g?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 1375,
+        "collected_count": 304,
+        "comment_count": 67,
+        "shared_count": 82
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子仅分享了一系列猫咪表情包图片,没有提供任何关于如何制作表情包的方法、技巧、工具或教程指导,不包含可复用的创作知识。",
+      "post_relevance_score": 0.3,
+      "relevance_level": "低度相关",
+      "relevance_reason": "帖子内容是猫咪表情包合集,与原始问题中的“猫咪表情包梗图”在主题上有所重叠。然而,原始问题是“如何制作”反映双标行为的表情包,而帖子仅为成品展示,并未提供任何制作方法或针对“双标行为”这一主题的指导,因此相关性较低,参考价值有限。"
+    },
+    "post_6826f7b7000000002202a171_5_8": {
+      "type": "post",
+      "query": "[R] 这组我是真喜欢|小猫|头像|表情包",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "6826f7b7000000002202a171",
+      "note_url": "https://www.xiaohongshu.com/explore/6826f7b7000000002202a171",
+      "body_text": "“这张卡你拿着的表情包,密码是0,余额也是。” 说,喜不喜欢我的大分享! 是鱼烧宝宝 #头像分享  #搞怪头像  #搞笑",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g2sg31hhnqdno3a0g5olmr77msvb54crgrt8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hhnqdno3a305olmr77msvb5ea1ihj8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hhnqdno3a405olmr77msvb5pu9gaf8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hhnqdno3a105olmr77msvb550s71sg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hhnqdno3a205olmr77msvb5a66bs38?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hhnqdno3a005olmr77msvb513882do?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hhnqdno3a2g5olmr77msvb59p4k9dg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hhnqdno3a1g5olmr77msvb55qp2400?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hhnqdno3a3g5olmr77msvb50mi7qvg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hhnqdno3a4g5olmr77msvb5439qkng?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hhnqdno3a5g5olmr77msvb5fabo07g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hhnqdno3a505olmr77msvb5bvdq410?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hhnqdno3a6g5olmr77msvb55rbl3cg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hhnqdno3a605olmr77msvb5ju5sipg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hhnveu2jaf05olmr77msvb5jntd7ho?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hhnveu2jaeg5olmr77msvb5oph4q08?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hhnveu2jae05olmr77msvb5fr06u8g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31hhnveu2jafg5olmr77msvb5bajd7i0?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hhnqdno3a0g5olmr77msvb54crgrt8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hhnqdno3a305olmr77msvb5ea1ihj8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hhnqdno3a405olmr77msvb5pu9gaf8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hhnqdno3a105olmr77msvb550s71sg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hhnqdno3a205olmr77msvb5a66bs38?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hhnqdno3a005olmr77msvb513882do?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hhnqdno3a2g5olmr77msvb59p4k9dg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hhnqdno3a1g5olmr77msvb55qp2400?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hhnqdno3a3g5olmr77msvb50mi7qvg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hhnqdno3a4g5olmr77msvb5439qkng?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hhnqdno3a5g5olmr77msvb5fabo07g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hhnqdno3a505olmr77msvb5bvdq410?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hhnqdno3a6g5olmr77msvb55rbl3cg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hhnqdno3a605olmr77msvb5ju5sipg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hhnveu2jaf05olmr77msvb5jntd7ho?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hhnveu2jaeg5olmr77msvb5oph4q08?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hhnveu2jae05olmr77msvb5fr06u8g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31hhnveu2jafg5olmr77msvb5bajd7i0?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 7358,
+        "collected_count": 2753,
+        "comment_count": 154,
+        "shared_count": 1771
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子是纯粹的表情包分享,不包含任何关于表情包制作方法、技巧或教程等可复用的创作知识。",
+      "post_relevance_score": 0.3,
+      "relevance_level": "低度相关",
+      "relevance_reason": "帖子内容展示了一些猫咪表情包,其中部分表情包(如“小人得志”、“世界的中指”)在一定程度上反映了人类情绪或态度,可能与“双标行为”略有边缘关联。但帖子未提供任何关于如何制作这类表情包、如何表现“双标行为”的创作知识,无法直接解决原问题。因此,相关性较低。"
+    },
+    "post_666d59f2000000000e032b89_5_9": {
+      "type": "post",
+      "query": "[R] 今日份表情包|我小猫咪听不懂你在讲什么",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "666d59f2000000000e032b89",
+      "note_url": "https://www.xiaohongshu.com/explore/666d59f2000000000e032b89",
+      "body_text": "这个话好难懂!真是难懂如维纳斯黄金比例般的奇迹我恨不得又哭又叫从五行山下嗖一声窜出来跑遍五岳爬珠穆朗玛峰跑哭来一个螺旋跪",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g2sg3142fqsmp26705oefu9g41i9fv0h5jo8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg3142fqsmp267g5oefu9g41i9fej3cc5o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg3142fqsmp26805oefu9g41i9fopeo190?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg3142fqsmp268g5oefu9g41i9fvo0lig0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg3142fqsmp26905oefu9g41i9fe15s1b8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg3142fqsmp269g5oefu9g41i9f7hchq28?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg3142fqsmp26a05oefu9g41i9fd9t8k3o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg3142fqsmp26ag5oefu9g41i9fmr12fq8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg3142fqsmp26b05oefu9g41i9fbnivupo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg3142fqsmp26bg5oefu9g41i9fmp7egtg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg3142fqsmp26c05oefu9g41i9f65kh8i8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg3142fqsmp26cg5oefu9g41i9fh4gi8bo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg3142fqsmp26d05oefu9g41i9fduh6vsg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg3142fqsmp26dg5oefu9g41i9fmdnfi1g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g0083142fqsob260g5oefu9g41i9fti4t3rg?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3142fqsmp26705oefu9g41i9fv0h5jo8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3142fqsmp267g5oefu9g41i9fej3cc5o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3142fqsmp26805oefu9g41i9fopeo190?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3142fqsmp268g5oefu9g41i9fvo0lig0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3142fqsmp26905oefu9g41i9fe15s1b8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3142fqsmp269g5oefu9g41i9f7hchq28?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3142fqsmp26a05oefu9g41i9fd9t8k3o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3142fqsmp26ag5oefu9g41i9fmr12fq8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3142fqsmp26b05oefu9g41i9fbnivupo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3142fqsmp26bg5oefu9g41i9fmp7egtg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3142fqsmp26c05oefu9g41i9f65kh8i8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3142fqsmp26cg5oefu9g41i9fh4gi8bo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3142fqsmp26d05oefu9g41i9fduh6vsg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg3142fqsmp26dg5oefu9g41i9fmdnfi1g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g0083142fqsob260g5oefu9g41i9fti4t3rg?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 1892,
+        "collected_count": 545,
+        "comment_count": 58,
+        "shared_count": 60
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子为纯粹的表情包分享和个人情感表达,未提供任何关于表情包创作的方法、技巧、工具使用步骤或教程指导,不具备可复用创作知识。",
+      "post_relevance_score": 0.4,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子内容包含猫咪表情包,与问题中的“猫咪表情包梗图”有一定表象关联。然而,帖子仅为表情包的集合与分享,并未针对如何制作或如何反映人类双标行为提供任何知识、方法或工具,因此参考价值有限。"
+    },
+    "search_猫咪表情包制作_r2_6": {
+      "type": "search_word",
+      "query": "[SEARCH] 猫咪表情包制作",
+      "level": 22,
+      "relevance_score": 0.825,
+      "strategy": "搜索词",
+      "iteration": 2,
+      "is_selected": true
+    },
+    "post_660581800000000012023abc_6_0": {
+      "type": "post",
+      "query": "[R] 表情包自制教程👀",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "660581800000000012023abc",
+      "note_url": "https://www.xiaohongshu.com/explore/660581800000000012023abc",
+      "body_text": "之前说过要出的教程虽迟但到🫶🏻 本来其实是用平板上的procreate 做的 但是考虑到软件普遍度的原因 所以用美图",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g2sg310t29mip6m005o6apeq08rkadafqerg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg310t29mip6m0g5o6apeq08rka1g0km40?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg310t29mip6m105o6apeq08rkakds9lco?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg310t29mip6m1g5o6apeq08rka4rhb3lo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg310t29mip6m205o6apeq08rka968chro?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg310t29mip6m005o6apeq08rkadafqerg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg310t29mip6m0g5o6apeq08rka1g0km40?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg310t29mip6m105o6apeq08rkakds9lco?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg310t29mip6m1g5o6apeq08rka4rhb3lo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg310t29mip6m205o6apeq08rka968chro?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 430,
+        "collected_count": 306,
+        "comment_count": 8,
+        "shared_count": 76
+      },
+      "extraction": null,
+      "is_knowledge": true,
+      "knowledge_reason": "该帖子提供了详细的表情包制作教程,包括工具选择(美图秀秀)、具体操作步骤(抠图、背景替换、贴纸、涂鸦),属于可复用的创作方法和工具使用指导。",
+      "post_relevance_score": 0.6,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子提供了制作猫咪表情包梗图的通用方法和工具指导,可以解决原始问题中的“如何制作”部分。但内容未涉及如何体现“人类双标行为”这一核心创意或主题,因此相关性中等,有一定参考价值但缺乏针对性。"
+    },
+    "post_678c8258000000001b00b500_6_1": {
+      "type": "post",
+      "query": "[R] 猫猫表情包",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "678c8258000000001b00b500",
+      "note_url": "https://www.xiaohongshu.com/explore/678c8258000000001b00b500",
+      "body_text": "制作了俺的小猫的表情包 😽可爱可爱😻 #宠物表情包  #圆脸小猫咪  #猫咪表情包  #可爱猫咪",
+      "images": [
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031cqt1iplgm0049qt9uv3tm8dlu39nj8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031cqt1iplgm0g49qt9uv3tm8dpjpoqc0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031cqt1iplgm1049qt9uv3tm8dcdr0bv8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031cqt1iplgm1g49qt9uv3tm8dkvpner0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031cqt1iplgm2049qt9uv3tm8df2bbjvo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031cqt1iplgm2g49qt9uv3tm8dmo1mi7g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031cqt1iplgm3049qt9uv3tm8dbu766uo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031cqt1iplgm3g49qt9uv3tm8dfodgsto?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031cqt1iplgm4049qt9uv3tm8d3d3gor8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031cqt1iplgm4g49qt9uv3tm8dq8vuc2o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031cqt1iplgm5049qt9uv3tm8d2u4e3n0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031cqt1iplgm5g49qt9uv3tm8dggfdj0o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031cqt1iplgm6049qt9uv3tm8dan96g7g?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031cqt1iplgm0049qt9uv3tm8dlu39nj8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031cqt1iplgm0g49qt9uv3tm8dpjpoqc0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031cqt1iplgm1049qt9uv3tm8dcdr0bv8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031cqt1iplgm1g49qt9uv3tm8dkvpner0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031cqt1iplgm2049qt9uv3tm8df2bbjvo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031cqt1iplgm2g49qt9uv3tm8dmo1mi7g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031cqt1iplgm3049qt9uv3tm8dbu766uo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031cqt1iplgm3g49qt9uv3tm8dfodgsto?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031cqt1iplgm4049qt9uv3tm8d3d3gor8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031cqt1iplgm4g49qt9uv3tm8dq8vuc2o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031cqt1iplgm5049qt9uv3tm8d2u4e3n0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031cqt1iplgm5g49qt9uv3tm8dggfdj0o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031cqt1iplgm6049qt9uv3tm8dan96g7g?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 216,
+        "collected_count": 76,
+        "comment_count": 12,
+        "shared_count": 17
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子是纯粹的个人分享和情感表达,展示了用户制作的猫咪表情包,但未提供任何关于表情包制作的方法、技巧、工具或流程等可复用的创作知识。",
+      "post_relevance_score": 0.4,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子内容展示了猫咪表情包,与原始问题中的“猫咪表情包”有表层关联,但其制作的表情包并非反映“人类双标行为”,且未提供任何制作猫咪表情包梗图的方法或思路,因此参考价值有限,仅为很低的启发性关联。"
+    },
+    "post_67f60c23000000001c03e8b0_6_2": {
+      "type": "post",
+      "query": "[R] 【猫主子の叛逆暴击!】当代恶猫表情包大赏",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "67f60c23000000001c03e8b0",
+      "note_url": "https://www.xiaohongshu.com/explore/67f60c23000000001c03e8b0",
+      "body_text": "教你如何制作自家猫咪搞怪表情包 1. 🙄《看垃圾的眼神》 配字:\"两脚兽又在加班?呵,小鱼干都凉透了\" 2. 😈《暗",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g00831g1v76c70g005orrhr2p2tihpq16vtg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831g1v76c70g0g5orrhr2p2tihne2ojm8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831g1v76c70g105orrhr2p2tih2kjqo48?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831g1v76c70g1g5orrhr2p2tihnnkqeb8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831g1v76c70g205orrhr2p2tih02pkcs0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831g1v76c70g2g5orrhr2p2tih5ggoa80?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831g1v76c70g305orrhr2p2tihrqr4juo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831g1v76c70g3g5orrhr2p2tihg25j6hg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831g1v76c70g405orrhr2p2tihm6urf5o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831g1v76c70g4g5orrhr2p2tihclmvqe8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831g1v76c70g505orrhr2p2tih4hprfio?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831g1v76c70g5g5orrhr2p2tihbkjn3v0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831g1v76c70g605orrhr2p2tih8jjbuco?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831g1v76c70g6g5orrhr2p2tih8h8493g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831g1v76ds0g005orrhr2p2tih92vgsgg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831g1v76ds0g0g5orrhr2p2tihnm2hu70?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831g1v76ds0g105orrhr2p2tiht8u3crg?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831g1v76c70g005orrhr2p2tihpq16vtg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831g1v76c70g0g5orrhr2p2tihne2ojm8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831g1v76c70g105orrhr2p2tih2kjqo48?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831g1v76c70g1g5orrhr2p2tihnnkqeb8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831g1v76c70g205orrhr2p2tih02pkcs0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831g1v76c70g2g5orrhr2p2tih5ggoa80?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831g1v76c70g305orrhr2p2tihrqr4juo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831g1v76c70g3g5orrhr2p2tihg25j6hg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831g1v76c70g405orrhr2p2tihm6urf5o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831g1v76c70g4g5orrhr2p2tihclmvqe8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831g1v76c70g505orrhr2p2tih4hprfio?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831g1v76c70g5g5orrhr2p2tihbkjn3v0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831g1v76c70g605orrhr2p2tih8jjbuco?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831g1v76c70g6g5orrhr2p2tih8h8493g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831g1v76ds0g005orrhr2p2tih92vgsgg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831g1v76ds0g0g5orrhr2p2tihnm2hu70?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831g1v76ds0g105orrhr2p2tiht8u3crg?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 105,
+        "collected_count": 53,
+        "comment_count": 0,
+        "shared_count": 7
+      },
+      "extraction": null,
+      "is_knowledge": true,
+      "knowledge_reason": "帖子正文第一句话明确指出“教你如何制作自家猫咪搞怪表情包”,提供了具体的表情包制作方法和配字思路,具有可复用的创作指导价值。",
+      "post_relevance_score": 0.85,
+      "relevance_level": "高度相关",
+      "relevance_reason": "该帖子直接提供了制作猫咪表情包梗图的实操教学和案例,特别是将猫咪行为与人类双标的配字结合,高度符合“制作反映人类双标行为的猫咪表情包梗图”的原始问题。内容针对性强,提供了创作方法和具体示例,虽然未穷尽所有双标场景,但指导性很强。"
+    },
+    "post_6618ef8a000000001b00815c_6_3": {
+      "type": "post",
+      "query": "[R] 当代年轻人的精神状态be like",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "6618ef8a000000001b00815c",
+      "note_url": "https://www.xiaohongshu.com/explore/6618ef8a000000001b00815c",
+      "body_text": "小猫咪演我精神状态[哭惹R] 给ee找了个制作表情包简易方法,教程在最后一页哦,请ee笑纳 #猫好人坏  #好猫俱乐部",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g008311g1fg8t6o0g40196vjp2cv3s5318b8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008311g1fg8t6o0040196vjp2cv3h58lt9g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008311g1fg8t6o1040196vjp2cv3fvtbao8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008311g1fg8tmo0040196vjp2cv311tthbg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008311g1fg8tmo0g40196vjp2cv3bape6fg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008311h7br706g0040196vjp2cv3vjmmqp8?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008311g1fg8t6o0g40196vjp2cv3s5318b8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008311g1fg8t6o0040196vjp2cv3h58lt9g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008311g1fg8t6o1040196vjp2cv3fvtbao8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008311g1fg8tmo0040196vjp2cv311tthbg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008311g1fg8tmo0g40196vjp2cv3bape6fg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008311h7br706g0040196vjp2cv3vjmmqp8?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 918,
+        "collected_count": 410,
+        "comment_count": 64,
+        "shared_count": 151
+      },
+      "extraction": null,
+      "is_knowledge": true,
+      "knowledge_reason": "帖子正文明确指出“教程在最后一页”,图片展示了使用美图秀秀制作猫咪表情包的简化流程,包含具体的工具使用和步骤指导。",
+      "post_relevance_score": 0.4,
+      "relevance_level": "中度相关",
+      "relevance_reason": "该帖子提供了制作猫咪表情包的方法,与原始问题“如何制作…猫咪表情包梗图”的制作类需求有一定关联。但内容主要围绕美图秀秀简单操作,未涉及如何反映“人类双标行为”这一核心创意主题,针对性和完整性不足。"
+    },
+    "post_68650c0e000000002400ec34_6_4": {
+      "type": "post",
+      "query": "[R] 自制小猫微信表情包一次过审分享",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "68650c0e000000002400ec34",
+      "note_url": "https://www.xiaohongshu.com/explore/68650c0e000000002400ec34",
+      "body_text": "先说总结,美图秀秀制作表情包+修改像素,醒图抠图用作聊天页图标,ios自带app”文件”里的快速操作——转换图像来修改图",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g2sg31jedrkp22o70433t274atqa5gjf140g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831je9pejg3400433t274atqa5m0uprgo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831je9pejg343g433t274atqa5q0g9tt0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831je9pejg344g433t274atqa5arv0to8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831je9pejg340g433t274atqa55hgil0o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831je9pejg3410433t274atqa5rjsf93o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831je9pejg345g433t274atqa55n3uae0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831je9pejg3420433t274atqa5vhi5a8g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831je9pejg3440433t274atqa5es21j70?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g00831je9pejg342g433t274atqa57o4gtto?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31jedrkp22o70433t274atqa5gjf140g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831je9pejg3400433t274atqa5m0uprgo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831je9pejg343g433t274atqa5q0g9tt0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831je9pejg344g433t274atqa5arv0to8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831je9pejg340g433t274atqa55hgil0o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831je9pejg3410433t274atqa5rjsf93o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831je9pejg345g433t274atqa55n3uae0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831je9pejg3420433t274atqa5vhi5a8g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831je9pejg3440433t274atqa5es21j70?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g00831je9pejg342g433t274atqa57o4gtto?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 373,
+        "collected_count": 330,
+        "comment_count": 51,
+        "shared_count": 117
+      },
+      "extraction": null,
+      "is_knowledge": true,
+      "knowledge_reason": "帖子提供了详细的表情包制作流程和工具使用方法,包括图片编辑、像素调整、抠图、文件格式转换等实用创作知识。",
+      "post_relevance_score": 0.5,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子提供了制作猫咪表情包的技术流程和工具,与原始问题中的“如何制作猫咪表情包”部分相关。但它未涉及“反映人类双标行为”这一创意核心,只是通用制作教程。"
+    },
+    "post_67541e26000000000800553c_6_5": {
+      "type": "post",
+      "query": "[R] ",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "67541e26000000000800553c",
+      "note_url": "https://www.xiaohongshu.com/explore/67541e26000000000800553c",
+      "body_text": "上班猫猫头也制作很简单,我用炫图ai抠下来了一些图像,我习惯先用一遍提画质,再进行抠图,抠出来的素材就会很清晰~还可以替",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g2sg31b3qtj367ae05p6u7u5g9t1ovte6nm8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31b3qtj367aeg5p6u7u5g9t1odhe09f0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31b3qtj367af05p6u7u5g9t1o2gngd7o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31b3qtj367afg5p6u7u5g9t1oub6b2m8?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31b3qtj367ae05p6u7u5g9t1ovte6nm8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31b3qtj367aeg5p6u7u5g9t1odhe09f0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31b3qtj367af05p6u7u5g9t1o2gngd7o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31b3qtj367afg5p6u7u5g9t1oub6b2m8?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 75,
+        "collected_count": 24,
+        "comment_count": 0,
+        "shared_count": 5
+      },
+      "extraction": null,
+      "is_knowledge": true,
+      "knowledge_reason": "帖子分享了使用'炫图ai'进行抠图和提画质的工具和操作步骤,属于图像处理的实用技巧,可复用于制作表情包等创作。",
+      "post_relevance_score": 0.4,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子内容与“制作反映人类双标行为的猫咪表情包梗图”的原始问题中关于“猫咪表情包梗图”的制作有一定关联性。它提供了基础的图像处理方法(抠图、提画质),这些是制作表情包的重要步骤。然而,帖子并未触及“反映人类双标行为”这一核心概念,也没有提供如何将猫咪形象与双标行为结合的创意或具体指导,因此相关性不高。"
+    },
+    "post_68ee4f9400000000050318ba_6_6": {
+      "type": "post",
+      "query": "[R] ",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "68ee4f9400000000050318ba",
+      "note_url": "https://www.xiaohongshu.com/explore/68ee4f9400000000050318ba",
+      "body_text": "无字白底猫memeP图素材来啦! 找图不易,取图喵喵喵[派对R] 期待宝宝的返图[皱眉R] 转载自@哥叫鱼烧你记住 #谁",
+      "images": [
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031nkddnnjmg6g5p3uoaf46r01iij4cm0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nkddnp0l8kg5p3uoaf46r01cee0iv8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nkddnrelm805p3uoaf46r01a4mqjq8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nkddnrelm7g5p3uoaf46r01emisi4o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nkddnrelm905p3uoaf46r01vvh1fmo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nkddnrelmag5p3uoaf46r01ol44jm0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nkddnrelmb05p3uoaf46r0168fvpmo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nkddnrelm9g5p3uoaf46r01fer6mv8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nkddnrelmbg5p3uoaf46r0130s6tu0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nkddnrelmc05p3uoaf46r01d5bgnpo?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nkddnrelmcg5p3uoaf46r0110avtco?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nkddnrelmd05p3uoaf46r01469p3j0?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nkddnrelmdg5p3uoaf46r015mpfhao?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k031nkddnnjmg6g5p3uoaf46r01iij4cm0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nkddnp0l8kg5p3uoaf46r01cee0iv8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nkddnrelm805p3uoaf46r01a4mqjq8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nkddnrelm7g5p3uoaf46r01emisi4o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nkddnrelm905p3uoaf46r01vvh1fmo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nkddnrelmag5p3uoaf46r01ol44jm0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nkddnrelmb05p3uoaf46r0168fvpmo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nkddnrelm9g5p3uoaf46r01fer6mv8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nkddnrelmbg5p3uoaf46r0130s6tu0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nkddnrelmc05p3uoaf46r01d5bgnpo?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nkddnrelmcg5p3uoaf46r0110avtco?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nkddnrelmd05p3uoaf46r01469p3j0?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/notes_pre_post/1040g3k831nkddnrelmdg5p3uoaf46r015mpfhao?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 120,
+        "collected_count": 48,
+        "comment_count": 4,
+        "shared_count": 14
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "帖子提供了P图素材,但没有包含可复用的创作方法、技巧、工具使用、流程步骤或教程指导等知识内容。",
+      "post_relevance_score": 0.5,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子提供了一系列猫咪P图素材,这些素材可以用于制作猫咪表情包梗图,与原始问题的目标制作梗图相关。但是,帖子并未直接指导如何制作反映人类双标行为的表情包,也没有提供具体的P图技巧或双标行为的体现方式,属于中度相关。"
+    },
+    "post_6803ae1a000000001c036da7_6_7": {
+      "type": "post",
+      "query": "[R] 可爱咪咪表情包第三弹",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "6803ae1a000000001c036da7",
+      "note_url": "https://www.xiaohongshu.com/explore/6803ae1a000000001c036da7",
+      "body_text": "自制表情包#猫咪表情包  #可爱猫咪  #谁说猫猫没有表情的  #表情包  #宠物表情包  #猫猫可以有多可爱  #猫猫",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g2sg31gf97stqjae05pk54p23cr3n6emm32o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31gf97stqjaeg5pk54p23cr3nk3d3mt8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31gf97stqjaf05pk54p23cr3n0tlsj2o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31gf97stqjafg5pk54p23cr3n64c1pso?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31gf97stqjag05pk54p23cr3nkjebcig?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31gf97stqjagg5pk54p23cr3nv4fnpu8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg31gf97stqjah05pk54p23cr3njutea58?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31gf97stqjae05pk54p23cr3n6emm32o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31gf97stqjaeg5pk54p23cr3nk3d3mt8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31gf97stqjaf05pk54p23cr3n0tlsj2o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31gf97stqjafg5pk54p23cr3n64c1pso?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31gf97stqjag05pk54p23cr3nkjebcig?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31gf97stqjagg5pk54p23cr3nv4fnpu8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg31gf97stqjah05pk54p23cr3njutea58?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 2888,
+        "collected_count": 631,
+        "comment_count": 47,
+        "shared_count": 80
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "该帖子是纯分享猫咪表情包的集合,没有提供任何关于如何制作表情包的方法、技巧、工具使用或详细的创作步骤指导,不包含可复用的创作知识。",
+      "post_relevance_score": 0.4,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子内容是猫咪表情包,与原始问题中的“猫咪表情包”元素相符,但并未提及如何反映“人类双标行为”,也没有提供任何制作此类表情包的具体方法或创作思路,相关性较低,仅提供素材参考价值。"
+    },
+    "post_66209a4c00000000040180ad_6_8": {
+      "type": "post",
+      "query": "[R] 有些图截着截着就变成了表情包",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "66209a4c00000000040180ad",
+      "note_url": "https://www.xiaohongshu.com/explore/66209a4c00000000040180ad",
+      "body_text": "唯爱自制表情包#被迫营业的小猫咪  #我家猫咪真可爱  #我的宠物真可爱  #表情包  #猫咪表情包  #羽毛球表情包",
+      "images": [
+        "https://ci.xiaohongshu.com/1040g008311nhq90ugce05n3u8tek38otlomtu78?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008311nh2fg60c0g5n3u8tek38otm0de2u8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008311nh2fg60c105n3u8tek38otsglbhr8?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008311nh2fg60c1g5n3u8tek38ot2la3drg?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008311nh2fg60c205n3u8tek38ot3p4hk8o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008311nh2fg60c2g5n3u8tek38otai81kig?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008311nh2fg60c305n3u8tek38ot4vt5c4o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008311nh2fg60c3g5n3u8tek38otss3q720?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008311nh2fg60c405n3u8tek38otb0j9rio?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008311nh2fg60c4g5n3u8tek38otai6pb9o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008311nh2fg60c505n3u8tek38otcu2593o?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008311nh2fg60c5g5n3u8tek38otg5gd95g?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008311nh2fg60c605n3u8tek38otlr4pi88?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g008311nh2fg60c6g5n3u8tek38ot59q9o08?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/1040g2sg311nh2fi80e005n3u8tek38otvioot4g?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008311nhq90ugce05n3u8tek38otlomtu78?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008311nh2fg60c0g5n3u8tek38otm0de2u8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008311nh2fg60c105n3u8tek38otsglbhr8?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008311nh2fg60c1g5n3u8tek38ot2la3drg?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008311nh2fg60c205n3u8tek38ot3p4hk8o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008311nh2fg60c2g5n3u8tek38otai81kig?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008311nh2fg60c305n3u8tek38ot4vt5c4o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008311nh2fg60c3g5n3u8tek38otss3q720?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008311nh2fg60c405n3u8tek38otb0j9rio?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008311nh2fg60c4g5n3u8tek38otai6pb9o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008311nh2fg60c505n3u8tek38otcu2593o?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008311nh2fg60c5g5n3u8tek38otg5gd95g?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008311nh2fg60c605n3u8tek38otlr4pi88?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g008311nh2fg60c6g5n3u8tek38ot59q9o08?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/1040g2sg311nh2fi80e005n3u8tek38otvioot4g?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 210,
+        "collected_count": 19,
+        "comment_count": 21,
+        "shared_count": 27
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "帖子属于个人日常分享和娱乐,展示了作者利用猫咪照片制作的表情包。内容没有提供任何关于表情包制作的步骤、技巧或工具使用方法,不包含可复用的创作知识。",
+      "post_relevance_score": 0.3,
+      "relevance_level": "低度相关",
+      "relevance_reason": "帖子内容与原始问题“如何制作反映人类双标行为的猫咪表情包梗图”相关性较低。帖子只是展示了作者制作的猫咪表情包,虽然是猫咪表情包,但没有提及“双标行为”的创作主题,也未提供任何制作方法或思路指导,无法解决原始问题。"
+    },
+    "post_618ff44f0000000001029e17_6_9": {
+      "type": "post",
+      "query": "[R] 猫咪大嘴表情包3",
+      "level": 23,
+      "relevance_score": 0,
+      "strategy": "帖子",
+      "iteration": 2,
+      "is_selected": true,
+      "note_id": "618ff44f0000000001029e17",
+      "note_url": "https://www.xiaohongshu.com/explore/618ff44f0000000001029e17",
+      "body_text": "可以自制表情包哦,可私拿原图,我登红薯🍠我都会回滴!!!#我最爱的表情包  #猫咪表情包",
+      "images": [
+        "https://ci.xiaohongshu.com/7f6bc9f2-45e2-37f5-94eb-41b72120ad56?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/30fe86dc-60af-3fc9-8573-03f27d7b4f56?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/78298768-e442-3a8c-b97f-7d917bc1f487?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/cba06721-b5f4-31e0-a138-390e6cc5a2de?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/f0cef77d-2d57-3bdd-8c6d-2e1b9173792a?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/fa9a52c3-62c0-34da-8578-7a8e15d679f9?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/fbc6daf0-b6dc-34f2-bf47-d7fa0205e4a7?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/7cbb8eaf-503f-389b-b576-5a6c87f3e6e2?imageView2/2/w/1080/format/webp",
+        "https://ci.xiaohongshu.com/6a909195-e455-3c54-bd61-7f4fba6c140e?imageView2/2/w/1080/format/webp"
+      ],
+      "image_list": [
+        {
+          "image_url": "https://ci.xiaohongshu.com/7f6bc9f2-45e2-37f5-94eb-41b72120ad56?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/30fe86dc-60af-3fc9-8573-03f27d7b4f56?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/78298768-e442-3a8c-b97f-7d917bc1f487?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/cba06721-b5f4-31e0-a138-390e6cc5a2de?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/f0cef77d-2d57-3bdd-8c6d-2e1b9173792a?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/fa9a52c3-62c0-34da-8578-7a8e15d679f9?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/fbc6daf0-b6dc-34f2-bf47-d7fa0205e4a7?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/7cbb8eaf-503f-389b-b576-5a6c87f3e6e2?imageView2/2/w/1080/format/webp"
+        },
+        {
+          "image_url": "https://ci.xiaohongshu.com/6a909195-e455-3c54-bd61-7f4fba6c140e?imageView2/2/w/1080/format/webp"
+        }
+      ],
+      "interact_info": {
+        "liked_count": 2930,
+        "collected_count": 790,
+        "comment_count": 216,
+        "shared_count": 163
+      },
+      "extraction": null,
+      "is_knowledge": false,
+      "knowledge_reason": "帖子正文仅表示可以私拿原图和自制表情包,并无提供任何关于表情包制作的方法、技巧、工具使用或流程步骤等具体创作知识。",
+      "post_relevance_score": 0.4,
+      "relevance_level": "中度相关",
+      "relevance_reason": "帖子提供了猫咪表情包的图片素材,但并未涉及如何反映人类双标行为的创意内容指导,也没有制作这类梗图的具体方法或工具,因此参考价值有限。"
+    },
+    "step_next_round_r2": {
+      "type": "step",
+      "query": "步骤4: 构建下一轮 (82个查询)",
+      "level": 21,
+      "relevance_score": 0,
+      "strategy": "构建下一轮",
+      "iteration": 2,
+      "is_selected": true
+    },
+    "next_round_制作猫咪表情包梗图_r2_0": {
+      "type": "next_round_item",
+      "query": "[Q] 制作猫咪表情包梗图",
+      "level": 22,
+      "relevance_score": 0.944,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+中心名词]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_制作猫咪表情包_r2_1": {
+      "type": "next_round_item",
+      "query": "[Q] 制作猫咪表情包",
+      "level": 22,
+      "relevance_score": 0.8800935999999999,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+中心名词]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_制作猫咪梗图_r2_2": {
+      "type": "next_round_item",
+      "query": "[Q] 制作猫咪梗图",
+      "level": 22,
+      "relevance_score": 0.8800935999999999,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+中心名词]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_制作表情包梗图_r2_3": {
+      "type": "next_round_item",
+      "query": "[Q] 制作表情包梗图",
+      "level": 22,
+      "relevance_score": 0.8146599999999999,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+中心名词]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_制作表情包_r2_4": {
+      "type": "next_round_item",
+      "query": "[Q] 制作表情包",
+      "level": 22,
+      "relevance_score": 0.7911999999999999,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+中心名词]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_制作反映人类双标行为_r2_5": {
+      "type": "next_round_item",
+      "query": "[Q] 制作反映人类双标行为",
+      "level": 22,
+      "relevance_score": 0.782236,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+修饰短语]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_制作反映人类双标_r2_6": {
+      "type": "next_round_item",
+      "query": "[Q] 制作反映人类双标",
+      "level": 22,
+      "relevance_score": 0.7630792,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+修饰短语]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_制作人类双标_r2_7": {
+      "type": "next_round_item",
+      "query": "[Q] 制作人类双标",
+      "level": 22,
+      "relevance_score": 0.7561959999999999,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+修饰短语]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_制作人类双标行为_r2_8": {
+      "type": "next_round_item",
+      "query": "[Q] 制作人类双标行为",
+      "level": 22,
+      "relevance_score": 0.7561959999999999,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+修饰短语]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_制作反映双标_r2_9": {
+      "type": "next_round_item",
+      "query": "[Q] 制作反映双标",
+      "level": 22,
+      "relevance_score": 0.7443309999999999,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+修饰短语]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_制作反映双标行为_r2_10": {
+      "type": "next_round_item",
+      "query": "[Q] 制作反映双标行为",
+      "level": 22,
+      "relevance_score": 0.7400964999999999,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+修饰短语]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_制作双标行为_r2_11": {
+      "type": "next_round_item",
+      "query": "[Q] 制作双标行为",
+      "level": 22,
+      "relevance_score": 0.7235799999999999,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+修饰短语]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_制作反映人类行为_r2_12": {
+      "type": "next_round_item",
+      "query": "[Q] 制作反映人类行为",
+      "level": 22,
+      "relevance_score": 0.717784,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[核心动作+修饰短语]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_反映人类双标猫咪表情包_r2_13": {
+      "type": "next_round_item",
+      "query": "[Q] 反映人类双标猫咪表情包",
+      "level": 22,
+      "relevance_score": 0.28565279999999993,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_反映双标猫咪梗图_r2_14": {
+      "type": "next_round_item",
+      "query": "[Q] 反映双标猫咪梗图",
+      "level": 22,
+      "relevance_score": 0.27439559999999996,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_如何猫咪表情包梗图_r2_15": {
+      "type": "next_round_item",
+      "query": "[Q] 如何猫咪表情包梗图",
+      "level": 22,
+      "relevance_score": 0.258,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+中心名词]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_反映人类双标行为表情包梗图_r2_16": {
+      "type": "next_round_item",
+      "query": "[Q] 反映人类双标行为表情包梗图",
+      "level": 22,
+      "relevance_score": 0.22783679999999998,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_如何猫咪表情包_r2_17": {
+      "type": "next_round_item",
+      "query": "[Q] 如何猫咪表情包",
+      "level": 22,
+      "relevance_score": 0.22427759999999997,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+中心名词]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_如何猫咪梗图_r2_18": {
+      "type": "next_round_item",
+      "query": "[Q] 如何猫咪梗图",
+      "level": 22,
+      "relevance_score": 0.22427759999999997,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+中心名词]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_反映双标行为表情包梗图_r2_19": {
+      "type": "next_round_item",
+      "query": "[Q] 反映双标行为表情包梗图",
+      "level": 22,
+      "relevance_score": 0.217728,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_反映行为猫咪梗图_r2_20": {
+      "type": "next_round_item",
+      "query": "[Q] 反映行为猫咪梗图",
+      "level": 22,
+      "relevance_score": 0.1881792,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_如何表情包梗图_r2_21": {
+      "type": "next_round_item",
+      "query": "[Q] 如何表情包梗图",
+      "level": 22,
+      "relevance_score": 0.1705725,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+中心名词]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_反映行为表情包梗图_r2_22": {
+      "type": "next_round_item",
+      "query": "[Q] 反映行为表情包梗图",
+      "level": 22,
+      "relevance_score": 0.153468,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_如何反映人类双标行为_r2_23": {
+      "type": "next_round_item",
+      "query": "[Q] 如何反映人类双标行为",
+      "level": 22,
+      "relevance_score": 0.1122,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+修饰短语]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_如何反映人类双标_r2_24": {
+      "type": "next_round_item",
+      "query": "[Q] 如何反映人类双标",
+      "level": 22,
+      "relevance_score": 0.109956,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+修饰短语]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_如何反映双标_r2_25": {
+      "type": "next_round_item",
+      "query": "[Q] 如何反映双标",
+      "level": 22,
+      "relevance_score": 0.10038000000000001,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+修饰短语]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_如何反映双标行为_r2_26": {
+      "type": "next_round_item",
+      "query": "[Q] 如何反映双标行为",
+      "level": 22,
+      "relevance_score": 0.096078,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+修饰短语]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_如何反映人类行为_r2_27": {
+      "type": "next_round_item",
+      "query": "[Q] 如何反映人类行为",
+      "level": 22,
+      "relevance_score": 0.080541,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+修饰短语]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_如何反映_r2_28": {
+      "type": "next_round_item",
+      "query": "[Q] 如何反映",
+      "level": 22,
+      "relevance_score": 0.03408,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+修饰短语]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_如何梗图_r2_29": {
+      "type": "next_round_item",
+      "query": "[Q] 如何梗图",
+      "level": 22,
+      "relevance_score": 0.0336,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+中心名词]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_如何人类双标行为_r2_30": {
+      "type": "next_round_item",
+      "query": "[Q] 如何人类双标行为",
+      "level": 22,
+      "relevance_score": 0.029925,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+修饰短语]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_如何反映行为_r2_31": {
+      "type": "next_round_item",
+      "query": "[Q] 如何反映行为",
+      "level": 22,
+      "relevance_score": 0.028423199999999996,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+修饰短语]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_如何人类双标_r2_32": {
+      "type": "next_round_item",
+      "query": "[Q] 如何人类双标",
+      "level": 22,
+      "relevance_score": 0.028350000000000004,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[疑问引导+修饰短语]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_反映人类双标梗图_r2_33": {
+      "type": "next_round_item",
+      "query": "[Q] 反映人类双标梗图",
+      "level": 22,
+      "relevance_score": 0.0262548,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_反映人类双标行为梗图_r2_34": {
+      "type": "next_round_item",
+      "query": "[Q] 反映人类双标行为梗图",
+      "level": 22,
+      "relevance_score": 0.0262548,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_反映双标梗图_r2_35": {
+      "type": "next_round_item",
+      "query": "[Q] 反映双标梗图",
+      "level": 22,
+      "relevance_score": 0.02457,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_人类双标梗图_r2_36": {
+      "type": "next_round_item",
+      "query": "[Q] 人类双标梗图",
+      "level": 22,
+      "relevance_score": 0.02457,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_人类双标行为梗图_r2_37": {
+      "type": "next_round_item",
+      "query": "[Q] 人类双标行为梗图",
+      "level": 22,
+      "relevance_score": 0.02457,
+      "strategy": "域内组合",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "[修饰短语+中心名词]",
+      "item_type": "combination",
+      "is_suggestion": false,
+      "suggestion_label": ""
+    },
+    "next_round_猫咪表情包制作_r2_38": {
+      "type": "next_round_item",
+      "query": "[Q] 猫咪表情包制作",
+      "level": 22,
+      "relevance_score": 0.825,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_梗图描改教程_r2_39": {
+      "type": "next_round_item",
+      "query": "[Q] 梗图描改教程",
+      "level": 22,
+      "relevance_score": 0.705,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_可爱又双标的表情包_r2_40": {
+      "type": "next_round_item",
+      "query": "[Q] 可爱又双标的表情包",
+      "level": 22,
+      "relevance_score": 0.68,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_怼双标的人表情包_r2_41": {
+      "type": "next_round_item",
+      "query": "[Q] 怼双标的人表情包",
+      "level": 22,
+      "relevance_score": 0.655,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_梗图meme原创_r2_42": {
+      "type": "next_round_item",
+      "query": "[Q] 梗图meme原创",
+      "level": 22,
+      "relevance_score": 0.654,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_猫咪表情包梗图搞笑_r2_43": {
+      "type": "next_round_item",
+      "query": "[Q] 猫咪表情包梗图搞笑",
+      "level": 22,
+      "relevance_score": 0.6000000000000001,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_猫咪表情包图片_r2_44": {
+      "type": "next_round_item",
+      "query": "[Q] 猫咪表情包图片",
+      "level": 22,
+      "relevance_score": 0.52,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_猫咪表情包图片_r2_45": {
+      "type": "next_round_item",
+      "query": "[Q] 猫咪表情包图片",
+      "level": 22,
+      "relevance_score": 0.52,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_讽刺双标的图片_r2_46": {
+      "type": "next_round_item",
+      "query": "[Q] 讽刺双标的图片",
+      "level": 22,
+      "relevance_score": 0.49,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_讽刺双标的图片_r2_47": {
+      "type": "next_round_item",
+      "query": "[Q] 讽刺双标的图片",
+      "level": 22,
+      "relevance_score": 0.49,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_双标表情包图片_r2_48": {
+      "type": "next_round_item",
+      "query": "[Q] 双标表情包图片",
+      "level": 22,
+      "relevance_score": 0.48,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_猫咪表情包_r2_49": {
+      "type": "next_round_item",
+      "query": "[Q] 猫咪表情包",
+      "level": 22,
+      "relevance_score": 0.48,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_梗图描改过程_r2_50": {
+      "type": "next_round_item",
+      "query": "[Q] 梗图描改过程",
+      "level": 22,
+      "relevance_score": 0.46,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_猫咪表情包视频_r2_51": {
+      "type": "next_round_item",
+      "query": "[Q] 猫咪表情包视频",
+      "level": 22,
+      "relevance_score": 0.455,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_猫咪搞笑表情包_r2_52": {
+      "type": "next_round_item",
+      "query": "[Q] 猫咪搞笑表情包",
+      "level": 22,
+      "relevance_score": 0.44999999999999996,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_反映人类双标行为的图片_r2_53": {
+      "type": "next_round_item",
+      "query": "[Q] 反映人类双标行为的图片",
+      "level": 22,
+      "relevance_score": 0.44000000000000006,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_猫咪双标行为_r2_54": {
+      "type": "next_round_item",
+      "query": "[Q] 猫咪双标行为",
+      "level": 22,
+      "relevance_score": 0.44000000000000006,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_梗图素材抽象_r2_55": {
+      "type": "next_round_item",
+      "query": "[Q] 梗图素材抽象",
+      "level": 22,
+      "relevance_score": 0.41000000000000003,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_猫咪咪表情包图片_r2_56": {
+      "type": "next_round_item",
+      "query": "[Q] 猫咪咪表情包图片",
+      "level": 22,
+      "relevance_score": 0.41000000000000003,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_猫咪表情包动图_r2_57": {
+      "type": "next_round_item",
+      "query": "[Q] 猫咪表情包动图",
+      "level": 22,
+      "relevance_score": 0.41000000000000003,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_表情包梗图动图_r2_58": {
+      "type": "next_round_item",
+      "query": "[Q] 表情包梗图动图",
+      "level": 22,
+      "relevance_score": 0.41000000000000003,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_梗图素材表情包_r2_59": {
+      "type": "next_round_item",
+      "query": "[Q] 梗图素材表情包",
+      "level": 22,
+      "relevance_score": 0.4,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_猫咪梗图素材_r2_60": {
+      "type": "next_round_item",
+      "query": "[Q] 猫咪梗图素材",
+      "level": 22,
+      "relevance_score": 0.4,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_猫咪梗图模板_r2_61": {
+      "type": "next_round_item",
+      "query": "[Q] 猫咪梗图模板",
+      "level": 22,
+      "relevance_score": 0.4,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_表情包梗图学习_r2_62": {
+      "type": "next_round_item",
+      "query": "[Q] 表情包梗图学习",
+      "level": 22,
+      "relevance_score": 0.39499999999999996,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_猫咪表情包配文_r2_63": {
+      "type": "next_round_item",
+      "query": "[Q] 猫咪表情包配文",
+      "level": 22,
+      "relevance_score": 0.38,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_梗图meme素材_r2_64": {
+      "type": "next_round_item",
+      "query": "[Q] 梗图meme素材",
+      "level": 22,
+      "relevance_score": 0.37,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_猫咪表情包可爱_r2_65": {
+      "type": "next_round_item",
+      "query": "[Q] 猫咪表情包可爱",
+      "level": 22,
+      "relevance_score": 0.37,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_表情包梗图抽象_r2_66": {
+      "type": "next_round_item",
+      "query": "[Q] 表情包梗图抽象",
+      "level": 22,
+      "relevance_score": 0.37,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_猫meme表情包动图_r2_67": {
+      "type": "next_round_item",
+      "query": "[Q] 猫meme表情包动图",
+      "level": 22,
+      "relevance_score": 0.36,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_猫咪梗图骂人_r2_68": {
+      "type": "next_round_item",
+      "query": "[Q] 猫咪梗图骂人",
+      "level": 22,
+      "relevance_score": 0.34500000000000003,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_梗图模版表情包_r2_69": {
+      "type": "next_round_item",
+      "query": "[Q] 梗图模版表情包",
+      "level": 22,
+      "relevance_score": 0.33000000000000007,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_猫咪梗图伤感_r2_70": {
+      "type": "next_round_item",
+      "query": "[Q] 猫咪梗图伤感",
+      "level": 22,
+      "relevance_score": 0.33000000000000007,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_猫咪双标行为搞笑视频_r2_71": {
+      "type": "next_round_item",
+      "query": "[Q] 猫咪双标行为搞笑视频",
+      "level": 22,
+      "relevance_score": 0.33,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_猫咪梗图拍照_r2_72": {
+      "type": "next_round_item",
+      "query": "[Q] 猫咪梗图拍照",
+      "level": 22,
+      "relevance_score": 0.29999999999999993,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_双标搞笑图片_r2_73": {
+      "type": "next_round_item",
+      "query": "[Q] 双标搞笑图片",
+      "level": 22,
+      "relevance_score": 0.29000000000000004,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_猫咪梗图英文_r2_74": {
+      "type": "next_round_item",
+      "query": "[Q] 猫咪梗图英文",
+      "level": 22,
+      "relevance_score": 0.29000000000000004,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_梗图描改模板_r2_75": {
+      "type": "next_round_item",
+      "query": "[Q] 梗图描改模板",
+      "level": 22,
+      "relevance_score": 0.28500000000000003,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_双标梗_r2_76": {
+      "type": "next_round_item",
+      "query": "[Q] 双标梗",
+      "level": 22,
+      "relevance_score": 0.27999999999999997,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_暗示双标的图片_r2_77": {
+      "type": "next_round_item",
+      "query": "[Q] 暗示双标的图片",
+      "level": 22,
+      "relevance_score": 0.24999999999999997,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_meme梗图分享_r2_78": {
+      "type": "next_round_item",
+      "query": "[Q] meme梗图分享",
+      "level": 22,
+      "relevance_score": 0.21000000000000002,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_双标行为举例_r2_79": {
+      "type": "next_round_item",
+      "query": "[Q] 双标行为举例",
+      "level": 22,
+      "relevance_score": 0.21,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_双标_r2_80": {
+      "type": "next_round_item",
+      "query": "[Q] 双标",
+      "level": 22,
+      "relevance_score": 0.2,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    },
+    "next_round_表情包图片大全素材_r2_81": {
+      "type": "next_round_item",
+      "query": "[Q] 表情包图片大全素材",
+      "level": 22,
+      "relevance_score": 0.19400000000000003,
+      "strategy": "高增益SUG",
+      "iteration": 2,
+      "is_selected": true,
+      "type_label": "",
+      "item_type": "sug",
+      "is_suggestion": true,
+      "suggestion_label": "[suggestion]"
+    }
+  },
+  "edges": [
+    {
+      "from": "root_o",
+      "to": "round_0",
+      "edge_type": "root_to_round",
+      "strategy": "初始化"
+    },
+    {
+      "from": "round_0",
+      "to": "step_seg_r0",
+      "edge_type": "round_to_step",
+      "strategy": "分段"
+    },
+    {
+      "from": "step_seg_r0",
+      "to": "segment_0_r0",
+      "edge_type": "step_to_segment",
+      "strategy": "疑问引导"
+    },
+    {
+      "from": "segment_0_r0",
+      "to": "word_如何_seg0_0",
+      "edge_type": "segment_to_word",
+      "strategy": "Word"
+    },
+    {
+      "from": "step_seg_r0",
+      "to": "segment_1_r0",
+      "edge_type": "step_to_segment",
+      "strategy": "核心动作"
+    },
+    {
+      "from": "segment_1_r0",
+      "to": "word_制作_seg1_0",
+      "edge_type": "segment_to_word",
+      "strategy": "Word"
+    },
+    {
+      "from": "step_seg_r0",
+      "to": "segment_2_r0",
+      "edge_type": "step_to_segment",
+      "strategy": "修饰短语"
+    },
+    {
+      "from": "segment_2_r0",
+      "to": "word_反映_seg2_0",
+      "edge_type": "segment_to_word",
+      "strategy": "Word"
+    },
+    {
+      "from": "segment_2_r0",
+      "to": "word_人类_seg2_1",
+      "edge_type": "segment_to_word",
+      "strategy": "Word"
+    },
+    {
+      "from": "segment_2_r0",
+      "to": "word_双标_seg2_2",
+      "edge_type": "segment_to_word",
+      "strategy": "Word"
+    },
+    {
+      "from": "segment_2_r0",
+      "to": "word_行为_seg2_3",
+      "edge_type": "segment_to_word",
+      "strategy": "Word"
+    },
+    {
+      "from": "step_seg_r0",
+      "to": "segment_3_r0",
+      "edge_type": "step_to_segment",
+      "strategy": "中心名词"
+    },
+    {
+      "from": "segment_3_r0",
+      "to": "word_猫咪_seg3_0",
+      "edge_type": "segment_to_word",
+      "strategy": "Word"
+    },
+    {
+      "from": "segment_3_r0",
+      "to": "word_表情包_seg3_1",
+      "edge_type": "segment_to_word",
+      "strategy": "Word"
+    },
+    {
+      "from": "segment_3_r0",
+      "to": "word_梗图_seg3_2",
+      "edge_type": "segment_to_word",
+      "strategy": "Word"
+    },
+    {
+      "from": "root_o",
+      "to": "round_1",
+      "edge_type": "root_to_round",
+      "strategy": "第1轮"
+    },
+    {
+      "from": "round_1",
+      "to": "step_sug_r1",
+      "edge_type": "round_to_step",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r1",
+      "to": "q_如何_r1_0",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_如何_r1_0",
+      "to": "sug_如何快速减肥_r1_q0_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_如何_r1_0",
+      "to": "sug_如何培养男人主动给你花钱_r1_q0_1",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_如何_r1_0",
+      "to": "sug_如何快速挣到钱_r1_q0_2",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_如何_r1_0",
+      "to": "sug_如何和女生聊天找话题_r1_q0_3",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_如何_r1_0",
+      "to": "sug_如何快速入睡_r1_q0_4",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_如何_r1_0",
+      "to": "sug_如何让男生持续上头_r1_q0_5",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_如何_r1_0",
+      "to": "sug_如何逼自己自律学习_r1_q0_6",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_如何_r1_0",
+      "to": "sug_如何连接别人家的加密wifi_r1_q0_7",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_如何_r1_0",
+      "to": "sug_如何养好头发_r1_q0_8",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_如何_r1_0",
+      "to": "sug_如何治疗早泻时间短_r1_q0_9",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r1",
+      "to": "q_制作_r1_1",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_制作_r1_1",
+      "to": "sug_制作ppt_r1_q1_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_制作_r1_1",
+      "to": "sug_制作表情包_r1_q1_1",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_制作_r1_1",
+      "to": "sug_制作冰糖葫芦_r1_q1_2",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_制作_r1_1",
+      "to": "sug_制作ppt的ai软件_r1_q1_3",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_制作_r1_1",
+      "to": "sug_制作视频_r1_q1_4",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_制作_r1_1",
+      "to": "sug_制作美食_r1_q1_5",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_制作_r1_1",
+      "to": "sug_制作简历_r1_q1_6",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_制作_r1_1",
+      "to": "sug_制作饮品_r1_q1_7",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_制作_r1_1",
+      "to": "sug_制作小房子_r1_q1_8",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_制作_r1_1",
+      "to": "sug_制作表格_r1_q1_9",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r1",
+      "to": "q_反映_r1_2",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_反映_r1_2",
+      "to": "sug_反映问题还是反应问题_r1_q2_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_反映_r1_2",
+      "to": "sug_反映和反应的区别_r1_q2_1",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_反映_r1_2",
+      "to": "sug_反映是什么意思_r1_q2_2",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_反映_r1_2",
+      "to": "sug_反映者_r1_q2_3",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_反映_r1_2",
+      "to": "sug_反应力小游戏_r1_q2_4",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_反映_r1_2",
+      "to": "sug_反应蛋白高是什么意思_r1_q2_5",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_反映_r1_2",
+      "to": "sug_反映拼音_r1_q2_6",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_反映_r1_2",
+      "to": "sug_反映财务状况的会计要素_r1_q2_7",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_反映_r1_2",
+      "to": "sug_反映的英语_r1_q2_8",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_反映_r1_2",
+      "to": "sug_反映事物间的互补关系_r1_q2_9",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r1",
+      "to": "q_人类_r1_3",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_人类_r1_3",
+      "to": "sug_人类一败涂地_r1_q3_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_人类_r1_3",
+      "to": "sug_人类狗窝_r1_q3_1",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_人类_r1_3",
+      "to": "sug_人类幼崽陪伴指南_r1_q3_2",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_人类_r1_3",
+      "to": "sug_人类用沙想捏出梦里通天塔_r1_q3_3",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_人类_r1_3",
+      "to": "sug_人类幼仔_r1_q3_4",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_人类_r1_3",
+      "to": "sug_人类简史_r1_q3_5",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_人类_r1_3",
+      "to": "sug_人类进化史_r1_q3_6",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_人类_r1_3",
+      "to": "sug_人类跌落梦境_r1_q3_7",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_人类_r1_3",
+      "to": "sug_人类群星闪耀时_r1_q3_8",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_人类_r1_3",
+      "to": "sug_人类高质量男姓_r1_q3_9",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r1",
+      "to": "q_双标_r1_4",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_双标_r1_4",
+      "to": "sug_双标是什么意思_r1_q4_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标_r1_4",
+      "to": "sug_讽刺双标的文案_r1_q4_1",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标_r1_4",
+      "to": "sug_双标的人是什么心理_r1_q4_2",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标_r1_4",
+      "to": "sug_双标文案_r1_q4_3",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标_r1_4",
+      "to": "sug_双标高爆卡点伴奏_r1_q4_4",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标_r1_4",
+      "to": "sug_双标信用卡_r1_q4_5",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标_r1_4",
+      "to": "sug_双椒兔做法_r1_q4_6",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标_r1_4",
+      "to": "sug_双标图片_r1_q4_7",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标_r1_4",
+      "to": "sug_双标表情包_r1_q4_8",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标_r1_4",
+      "to": "sug_双标的人_r1_q4_9",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r1",
+      "to": "q_行为_r1_5",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_行为_r1_5",
+      "to": "sug_行为心理学_r1_q5_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_行为_r1_5",
+      "to": "sug_行为基础_r1_q5_1",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_行为_r1_5",
+      "to": "sug_行为规范手抄报_r1_q5_2",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_行为_r1_5",
+      "to": "sug_行为习惯手抄报_r1_q5_3",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_行为_r1_5",
+      "to": "sug_行为艺术_r1_q5_4",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_行为_r1_5",
+      "to": "sug_行为决定关系而非关系决定行为_r1_q5_5",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_行为_r1_5",
+      "to": "sug_行为违反腾讯用户协议_r1_q5_6",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_行为_r1_5",
+      "to": "sug_行为认知疗法_r1_q5_7",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_行为_r1_5",
+      "to": "sug_行为经济学_r1_q5_8",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_行为_r1_5",
+      "to": "sug_行为规范家_r1_q5_9",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r1",
+      "to": "q_猫咪_r1_6",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_猫咪_r1_6",
+      "to": "sug_猫咪领养免费领养_r1_q6_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪_r1_6",
+      "to": "sug_猫咪叫声吸引小猫_r1_q6_1",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪_r1_6",
+      "to": "sug_猫咪呕吐_r1_q6_2",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪_r1_6",
+      "to": "sug_猫咪头像_r1_q6_3",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪_r1_6",
+      "to": "sug_猫咪取名_r1_q6_4",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪_r1_6",
+      "to": "sug_猫咪品种_r1_q6_5",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪_r1_6",
+      "to": "sug_猫咪搞笑视频_r1_q6_6",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪_r1_6",
+      "to": "sug_猫咪叫声_r1_q6_7",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪_r1_6",
+      "to": "sug_猫咪驱虫药推荐_r1_q6_8",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪_r1_6",
+      "to": "sug_猫咪黑下巴怎么处理_r1_q6_9",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r1",
+      "to": "q_表情包_r1_7",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_表情包_r1_7",
+      "to": "sug_表情包抽象_r1_q7_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包_r1_7",
+      "to": "sug_表情包怎么制作_r1_q7_1",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包_r1_7",
+      "to": "sug_表情包可爱_r1_q7_2",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包_r1_7",
+      "to": "sug_表情包图片大全_r1_q7_3",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包_r1_7",
+      "to": "sug_表情包搞笑配文_r1_q7_4",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包_r1_7",
+      "to": "sug_表情包发给女朋友_r1_q7_5",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包_r1_7",
+      "to": "sug_表情包简笔画_r1_q7_6",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包_r1_7",
+      "to": "sug_表情包模板_r1_q7_7",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包_r1_7",
+      "to": "sug_表情包发给男朋友_r1_q7_8",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包_r1_7",
+      "to": "sug_表情包制作赚钱_r1_q7_9",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r1",
+      "to": "q_梗图_r1_8",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_梗图_r1_8",
+      "to": "sug_梗图素材_r1_q8_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图_r1_8",
+      "to": "sug_梗图搞笑_r1_q8_1",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图_r1_8",
+      "to": "sug_梗图精神状态_r1_q8_2",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图_r1_8",
+      "to": "sug_梗图meme_r1_q8_3",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图_r1_8",
+      "to": "sug_梗图双人_r1_q8_4",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图_r1_8",
+      "to": "sug_梗图抽象_r1_q8_5",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图_r1_8",
+      "to": "sug_梗图描改_r1_q8_6",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图_r1_8",
+      "to": "sug_梗图模版_r1_q8_7",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图_r1_8",
+      "to": "sug_梗图分享_r1_q8_8",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图_r1_8",
+      "to": "sug_梗图大全_r1_q8_9",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "round_1",
+      "to": "step_comb_r1",
+      "edge_type": "round_to_step",
+      "strategy": "域内组词"
+    },
+    {
+      "from": "step_comb_r1",
+      "to": "comb_反映人类_r1_0",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r1",
+      "to": "comb_反映双标_r1_1",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r1",
+      "to": "comb_反映行为_r1_2",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r1",
+      "to": "comb_人类双标_r1_3",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r1",
+      "to": "comb_人类行为_r1_4",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r1",
+      "to": "comb_双标行为_r1_5",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r1",
+      "to": "comb_反映人类双标_r1_6",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r1",
+      "to": "comb_反映人类行为_r1_7",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r1",
+      "to": "comb_反映双标行为_r1_8",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r1",
+      "to": "comb_人类双标行为_r1_9",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r1",
+      "to": "comb_反映人类双标行为_r1_10",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r1",
+      "to": "comb_猫咪表情包_r1_11",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r1",
+      "to": "comb_猫咪梗图_r1_12",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r1",
+      "to": "comb_表情包梗图_r1_13",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r1",
+      "to": "comb_猫咪表情包梗图_r1_14",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "round_1",
+      "to": "step_search_r1",
+      "edge_type": "round_to_step",
+      "strategy": "搜索"
+    },
+    {
+      "from": "step_search_r1",
+      "to": "search_制作表情包_r1_0",
+      "edge_type": "step_to_search_word",
+      "strategy": "搜索词"
+    },
+    {
+      "from": "search_制作表情包_r1_0",
+      "to": "post_66f9046a000000002a033202_0_0",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_制作表情包_r1_0",
+      "to": "post_668e06cd00000000250145fe_0_1",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_制作表情包_r1_0",
+      "to": "post_68282bbd000000002001f349_0_2",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_制作表情包_r1_0",
+      "to": "post_67d1b6c9000000002802a550_0_3",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_制作表情包_r1_0",
+      "to": "post_68ef01ad000000000302f388_0_4",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_制作表情包_r1_0",
+      "to": "post_66ea4e30000000001e01964b_0_5",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_制作表情包_r1_0",
+      "to": "post_67c72549000000000602a376_0_6",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_制作表情包_r1_0",
+      "to": "post_6876f73f0000000011002f0c_0_7",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_制作表情包_r1_0",
+      "to": "post_6613c01f000000001b00820c_0_8",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_制作表情包_r1_0",
+      "to": "post_61b81195000000002103b7ab_0_9",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "step_search_r1",
+      "to": "search_表情包怎么制作_r1_1",
+      "edge_type": "step_to_search_word",
+      "strategy": "搜索词"
+    },
+    {
+      "from": "round_1",
+      "to": "step_next_round_r1",
+      "edge_type": "round_to_step",
+      "strategy": "构建下一轮"
+    },
+    {
+      "from": "step_next_round_r1",
+      "to": "next_round_猫咪表情包梗图_r1_0",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r1",
+      "to": "next_round_猫咪表情包_r1_1",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r1",
+      "to": "next_round_猫咪梗图_r1_2",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r1",
+      "to": "next_round_表情包梗图_r1_3",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r1",
+      "to": "next_round_反映人类双标_r1_4",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r1",
+      "to": "next_round_反映人类双标行为_r1_5",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r1",
+      "to": "next_round_反映双标_r1_6",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r1",
+      "to": "next_round_人类双标_r1_7",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r1",
+      "to": "next_round_人类双标行为_r1_8",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r1",
+      "to": "next_round_双标行为_r1_9",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r1",
+      "to": "next_round_反映双标行为_r1_10",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r1",
+      "to": "next_round_反映人类_r1_11",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r1",
+      "to": "next_round_反映人类行为_r1_12",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r1",
+      "to": "next_round_人类行为_r1_13",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r1",
+      "to": "next_round_制作表情包_r1_14",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r1",
+      "to": "next_round_表情包怎么制作_r1_15",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r1",
+      "to": "next_round_双标表情包_r1_16",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r1",
+      "to": "next_round_梗图素材_r1_17",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r1",
+      "to": "next_round_梗图模版_r1_18",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r1",
+      "to": "next_round_梗图描改_r1_19",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r1",
+      "to": "next_round_表情包简笔画_r1_20",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r1",
+      "to": "next_round_双标图片_r1_21",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r1",
+      "to": "next_round_表情包图片大全_r1_22",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r1",
+      "to": "next_round_梗图meme_r1_23",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r1",
+      "to": "next_round_梗图分享_r1_24",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "root_o",
+      "to": "round_2",
+      "edge_type": "root_to_round",
+      "strategy": "第2轮"
+    },
+    {
+      "from": "round_2",
+      "to": "step_sug_r2",
+      "edge_type": "round_to_step",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r2",
+      "to": "q_制作表情包_r2_0",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_制作表情包_r2_0",
+      "to": "sug_制作表情包用什么软件_r2_q0_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_制作表情包_r2_0",
+      "to": "sug_制作表情包微信_r2_q0_1",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_制作表情包_r2_0",
+      "to": "sug_制作表情包怎么赚钱_r2_q0_2",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_制作表情包_r2_0",
+      "to": "sug_制作表情包教程_r2_q0_3",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_制作表情包_r2_0",
+      "to": "sug_制作表情包软件_r2_q0_4",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_制作表情包_r2_0",
+      "to": "sug_制作表情包gif_r2_q0_5",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_制作表情包_r2_0",
+      "to": "sug_制作表情包动图_r2_q0_6",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_制作表情包_r2_0",
+      "to": "sug_视频制作表情包_r2_q0_7",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_制作表情包_r2_0",
+      "to": "sug_美图秀秀制作表情包图片_r2_q0_8",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_制作表情包_r2_0",
+      "to": "sug_制作表情包文字_r2_q0_9",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r2",
+      "to": "q_表情包怎么制作_r2_1",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_表情包怎么制作_r2_1",
+      "to": "sug_表情包怎么制作gif_r2_q1_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包怎么制作_r2_1",
+      "to": "sug_表情包怎么制作动态_r2_q1_1",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包怎么制作_r2_1",
+      "to": "sug_表情包怎么制作微信_r2_q1_2",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包怎么制作_r2_1",
+      "to": "sug_表情包怎么制作文字_r2_q1_3",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包怎么制作_r2_1",
+      "to": "sug_小红书表情包怎么制作的_r2_q1_4",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包怎么制作_r2_1",
+      "to": "sug_原创表情包怎么制作_r2_q1_5",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包怎么制作_r2_1",
+      "to": "sug_表情包怎么制作视频_r2_q1_6",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包怎么制作_r2_1",
+      "to": "sug_表情包怎么制作动态图_r2_q1_7",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包怎么制作_r2_1",
+      "to": "sug_表情包怎么制作动图_r2_q1_8",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包怎么制作_r2_1",
+      "to": "sug_制作表情包怎么赚钱_r2_q1_9",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r2",
+      "to": "q_双标表情包_r2_2",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_双标表情包_r2_2",
+      "to": "sug_双标表情包内涵_r2_q2_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标表情包_r2_2",
+      "to": "sug_双标表情包图片_r2_q2_1",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标表情包_r2_2",
+      "to": "sug_怼双标的人表情包_r2_q2_2",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标表情包_r2_2",
+      "to": "sug_可爱又双标的表情包_r2_q2_3",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标表情包_r2_2",
+      "to": "sug_讽刺双标的图片_r2_q2_4",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标表情包_r2_2",
+      "to": "sug_双标梗图模板_r2_q2_5",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标表情包_r2_2",
+      "to": "sug_讽刺双标的人的文案_r2_q2_6",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标表情包_r2_2",
+      "to": "sug_很双标的朋友圈文案_r2_q2_7",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标表情包_r2_2",
+      "to": "sug_这可是要砍头的表情包_r2_q2_8",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标表情包_r2_2",
+      "to": "sug_双标的人怎么怼_r2_q2_9",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r2",
+      "to": "q_梗图素材_r2_3",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_梗图素材_r2_3",
+      "to": "sug_梗图素材模板_r2_q3_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图素材_r2_3",
+      "to": "sug_梗图素材抽象_r2_q3_1",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图素材_r2_3",
+      "to": "sug_梗图素材双人_r2_q3_2",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图素材_r2_3",
+      "to": "sug_梗图素材单人_r2_q3_3",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图素材_r2_3",
+      "to": "sug_梗图素材原图_r2_q3_4",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图素材_r2_3",
+      "to": "sug_梗图素材多人_r2_q3_5",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图素材_r2_3",
+      "to": "sug_梗图素材画画_r2_q3_6",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图素材_r2_3",
+      "to": "sug_梗图素材描改_r2_q3_7",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图素材_r2_3",
+      "to": "sug_梗图素材表情包_r2_q3_8",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图素材_r2_3",
+      "to": "sug_梗图素材聊天记录_r2_q3_9",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r2",
+      "to": "q_梗图模版_r2_4",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_梗图模版_r2_4",
+      "to": "sug_梗图模版抽象_r2_q4_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图模版_r2_4",
+      "to": "sug_梗图模版双人_r2_q4_1",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图模版_r2_4",
+      "to": "sug_梗图模版单人_r2_q4_2",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图模版_r2_4",
+      "to": "sug_梗图模板_r2_q4_3",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图模版_r2_4",
+      "to": "sug_梗图模版三人_r2_q4_4",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图模版_r2_4",
+      "to": "sug_梗图模版表情包_r2_q4_5",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图模版_r2_4",
+      "to": "sug_梗图模版多人_r2_q4_6",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图模版_r2_4",
+      "to": "sug_梗图模版四人_r2_q4_7",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图模版_r2_4",
+      "to": "sug_双眼皮模版图_r2_q4_8",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图模版_r2_4",
+      "to": "sug_梗图模版表格_r2_q4_9",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r2",
+      "to": "q_梗图描改_r2_5",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_梗图描改_r2_5",
+      "to": "sug_梗图描改接稿_r2_q5_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图描改_r2_5",
+      "to": "sug_梗图描改素材_r2_q5_1",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图描改_r2_5",
+      "to": "sug_梗图描改教程_r2_q5_2",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图描改_r2_5",
+      "to": "sug_梗图描改怎么画_r2_q5_3",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图描改_r2_5",
+      "to": "sug_梗图描改模板_r2_q5_4",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图描改_r2_5",
+      "to": "sug_梗图描改约稿_r2_q5_5",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图描改_r2_5",
+      "to": "sug_梗图描改原图_r2_q5_6",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图描改_r2_5",
+      "to": "sug_梗图描改鸡蛋_r2_q5_7",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图描改_r2_5",
+      "to": "sug_抽象梗图描改_r2_q5_8",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图描改_r2_5",
+      "to": "sug_梗图描改过程_r2_q5_9",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r2",
+      "to": "q_表情包简笔画_r2_6",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_表情包简笔画_r2_6",
+      "to": "sug_表情包简笔画可爱_r2_q6_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包简笔画_r2_6",
+      "to": "sug_表情包简笔画抽象_r2_q6_1",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包简笔画_r2_6",
+      "to": "sug_表情包简笔画大全_r2_q6_2",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包简笔画_r2_6",
+      "to": "sug_表情包简笔画哭_r2_q6_3",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包简笔画_r2_6",
+      "to": "sug_表情包简笔画图片_r2_q6_4",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包简笔画_r2_6",
+      "to": "sug_表情包简笔画人物_r2_q6_5",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包简笔画_r2_6",
+      "to": "sug_表情包简笔画教程_r2_q6_6",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包简笔画_r2_6",
+      "to": "sug_表情简笔画_r2_q6_7",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包简笔画_r2_6",
+      "to": "sug_表情包简笔画加油_r2_q6_8",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包简笔画_r2_6",
+      "to": "sug_表情包简笔画开心_r2_q6_9",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r2",
+      "to": "q_双标图片_r2_7",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_双标图片_r2_7",
+      "to": "sug_讽刺双标的图片_r2_q7_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标图片_r2_7",
+      "to": "sug_暗示双标的图片_r2_q7_1",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标图片_r2_7",
+      "to": "sug_双标搞笑图片_r2_q7_2",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标图片_r2_7",
+      "to": "sug_讽刺双标的人的文案_r2_q7_3",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标图片_r2_7",
+      "to": "sug_双标梗_r2_q7_4",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标图片_r2_7",
+      "to": "sug_双标是什么意思_r2_q7_5",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标图片_r2_7",
+      "to": "sug_形容双标人的文案_r2_q7_6",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标图片_r2_7",
+      "to": "sug_内涵双标的文案_r2_q7_7",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标图片_r2_7",
+      "to": "sug_卡皮巴拉100种可爱图片_r2_q7_8",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标图片_r2_7",
+      "to": "sug_双标_r2_q7_9",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r2",
+      "to": "q_表情包图片大全_r2_8",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_表情包图片大全_r2_8",
+      "to": "sug_表情包图片大全简笔画_r2_q8_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包图片大全_r2_8",
+      "to": "sug_表情包图片大全微信_r2_q8_1",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包图片大全_r2_8",
+      "to": "sug_表情包图片大全抽象_r2_q8_2",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包图片大全_r2_8",
+      "to": "sug_表情包图片大全可爱_r2_q8_3",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包图片大全_r2_8",
+      "to": "sug_表情包图片大全动态_r2_q8_4",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包图片大全_r2_8",
+      "to": "sug_表情包图片大全素材_r2_q8_5",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包图片大全_r2_8",
+      "to": "sug_表情包图片大全搞笑_r2_q8_6",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包图片大全_r2_8",
+      "to": "sug_ch表情包图片大全_r2_q8_7",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包图片大全_r2_8",
+      "to": "sug_开心表情包图片大全_r2_q8_8",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包图片大全_r2_8",
+      "to": "sug_表情包图片大全打印素材_r2_q8_9",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r2",
+      "to": "q_梗图meme_r2_9",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_梗图meme_r2_9",
+      "to": "sug_梗图meme双人_r2_q9_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图meme_r2_9",
+      "to": "sug_梗图meme模板_r2_q9_1",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图meme_r2_9",
+      "to": "sug_心理疾病meme梗图_r2_q9_2",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图meme_r2_9",
+      "to": "sug_梗图meme素材_r2_q9_3",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图meme_r2_9",
+      "to": "sug_梗图meme悲伤自嘲_r2_q9_4",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图meme_r2_9",
+      "to": "sug_梗图meme代餐_r2_q9_5",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图meme_r2_9",
+      "to": "sug_梗图meme学习_r2_q9_6",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图meme_r2_9",
+      "to": "sug_焦虑症meme梗图_r2_q9_7",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图meme_r2_9",
+      "to": "sug_精神疾病meme梗图_r2_q9_8",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图meme_r2_9",
+      "to": "sug_梗图meme原创_r2_q9_9",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r2",
+      "to": "q_梗图分享_r2_10",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_梗图分享_r2_10",
+      "to": "sug_恋与深空同人图资源分享_r2_q10_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图分享_r2_10",
+      "to": "sug_早安分享图片_r2_q10_1",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图分享_r2_10",
+      "to": "sug_定制分享图_r2_q10_2",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图分享_r2_10",
+      "to": "sug_图纸分享_r2_q10_3",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图分享_r2_10",
+      "to": "sug_meme梗图分享_r2_q10_4",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图分享_r2_10",
+      "to": "sug_拼豆豆图纸分享_r2_q10_5",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图分享_r2_10",
+      "to": "sug_买断图分享_r2_q10_6",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图分享_r2_10",
+      "to": "sug_拼豆图纸分享_r2_q10_7",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图分享_r2_10",
+      "to": "sug_日常分享图片_r2_q10_8",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_梗图分享_r2_10",
+      "to": "sug_自截分享图_r2_q10_9",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r2",
+      "to": "q_猫咪表情包梗图_r2_11",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_猫咪表情包梗图_r2_11",
+      "to": "sug_猫咪表情包_r2_q11_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪表情包梗图_r2_11",
+      "to": "sug_猫咪表情包图片_r2_q11_1",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪表情包梗图_r2_11",
+      "to": "sug_猫咪咪表情包图片_r2_q11_2",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪表情包梗图_r2_11",
+      "to": "sug_猫咪搞笑表情包_r2_q11_3",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪表情包梗图_r2_11",
+      "to": "sug_猫咪表情包梗图搞笑_r2_q11_4",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r2",
+      "to": "q_猫咪表情包_r2_12",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_猫咪表情包_r2_12",
+      "to": "sug_猫咪表情包抽象_r2_q12_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪表情包_r2_12",
+      "to": "sug_猫meme表情包动图_r2_q12_1",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪表情包_r2_12",
+      "to": "sug_猫咪表情包配文_r2_q12_2",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪表情包_r2_12",
+      "to": "sug_猫咪表情包动图_r2_q12_3",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪表情包_r2_12",
+      "to": "sug_猫咪表情包制作_r2_q12_4",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪表情包_r2_12",
+      "to": "sug_猫咪表情包可爱_r2_q12_5",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪表情包_r2_12",
+      "to": "sug_猫咪表情符号_r2_q12_6",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪表情包_r2_12",
+      "to": "sug_猫咪表情包叫什么_r2_q12_7",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪表情包_r2_12",
+      "to": "sug_猫咪表情包图片_r2_q12_8",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪表情包_r2_12",
+      "to": "sug_猫咪表情包视频_r2_q12_9",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r2",
+      "to": "q_猫咪梗图_r2_13",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_猫咪梗图_r2_13",
+      "to": "sug_猫咪梗图素材_r2_q13_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪梗图_r2_13",
+      "to": "sug_猫咪梗图模板_r2_q13_1",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪梗图_r2_13",
+      "to": "sug_猫咪梗图骂人_r2_q13_2",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪梗图_r2_13",
+      "to": "sug_猫咪梗图恋爱_r2_q13_3",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪梗图_r2_13",
+      "to": "sug_猫咪梗图伤感_r2_q13_4",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪梗图_r2_13",
+      "to": "sug_猫咪梗图睡觉_r2_q13_5",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪梗图_r2_13",
+      "to": "sug_猫咪梗图英文_r2_q13_6",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪梗图_r2_13",
+      "to": "sug_猫咪梗图塔牌_r2_q13_7",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪梗图_r2_13",
+      "to": "sug_猫咪梗图拍照_r2_q13_8",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_猫咪梗图_r2_13",
+      "to": "sug_猫咪梗图上帝_r2_q13_9",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r2",
+      "to": "q_表情包梗图_r2_14",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_表情包梗图_r2_14",
+      "to": "sug_表情包梗图模板_r2_q14_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包梗图_r2_14",
+      "to": "sug_表情包梗图抽象_r2_q14_1",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包梗图_r2_14",
+      "to": "sug_jojo表情包梗图_r2_q14_2",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包梗图_r2_14",
+      "to": "sug_表情包梗图学习_r2_q14_3",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包梗图_r2_14",
+      "to": "sug_表情包梗图动图_r2_q14_4",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包梗图_r2_14",
+      "to": "sug_表情包梗图cp_r2_q14_5",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包梗图_r2_14",
+      "to": "sug_重返未来表情包梗图_r2_q14_6",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包梗图_r2_14",
+      "to": "sug_表情包梗图可爱_r2_q14_7",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包梗图_r2_14",
+      "to": "sug_表情包梗图恶俗_r2_q14_8",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_表情包梗图_r2_14",
+      "to": "sug_表情包梗图开心_r2_q14_9",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r2",
+      "to": "q_反映人类双标_r2_15",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_反映人类双标_r2_15",
+      "to": "sug_反映人类双胞胎基因变异_r2_q15_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r2",
+      "to": "q_反映人类双标行为_r2_16",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_反映人类双标行为_r2_16",
+      "to": "sug_反映人类双标行为的图片_r2_q16_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r2",
+      "to": "q_人类双标行为_r2_17",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_人类双标行为_r2_17",
+      "to": "sug_人类双标行为是什么意思_r2_q17_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r2",
+      "to": "q_双标行为_r2_18",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_双标行为_r2_18",
+      "to": "sug_双标行为举例_r2_q18_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标行为_r2_18",
+      "to": "sug_双标行为内涵是什么_r2_q18_1",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标行为_r2_18",
+      "to": "sug_讽刺双标的文案_r2_q18_2",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标行为_r2_18",
+      "to": "sug_王楚钦双标行为_r2_q18_3",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标行为_r2_18",
+      "to": "sug_猫咪为什么会出现双标行为_r2_q18_4",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标行为_r2_18",
+      "to": "sug_双标男友的典型表现_r2_q18_5",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标行为_r2_18",
+      "to": "sug_猫咪双标行为搞笑视频_r2_q18_6",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标行为_r2_18",
+      "to": "sug_猫咪双标行为_r2_q18_7",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标行为_r2_18",
+      "to": "sug_双标是什么意思啊_r2_q18_8",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_双标行为_r2_18",
+      "to": "sug_双标的人怎么怼_r2_q18_9",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r2",
+      "to": "q_反映人类_r2_19",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_反映人类_r2_19",
+      "to": "sug_反映人类社会发展进步的价值理念_r2_q19_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_反映人类_r2_19",
+      "to": "sug_人类图反映者_r2_q19_1",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_反映人类_r2_19",
+      "to": "sug_怎么识别人类图中的反映者_r2_q19_2",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_反映人类_r2_19",
+      "to": "sug_人类图显示者_r2_q19_3",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_反映人类_r2_19",
+      "to": "sug_人类图解读_r2_q19_4",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_反映人类_r2_19",
+      "to": "sug_人类图是什么_r2_q19_5",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_反映人类_r2_19",
+      "to": "sug_反映者适合什么工作_r2_q19_6",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_反映人类_r2_19",
+      "to": "sug_人类图投射者_r2_q19_7",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_反映人类_r2_19",
+      "to": "sug_反映者的代表人物_r2_q19_8",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_反映人类_r2_19",
+      "to": "sug_人类图测试_r2_q19_9",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r2",
+      "to": "q_反映人类行为_r2_20",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_反映人类行为_r2_20",
+      "to": "sug_反映人类行为的环境问题_r2_q20_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "step_sug_r2",
+      "to": "q_人类行为_r2_21",
+      "edge_type": "step_to_q",
+      "strategy": "Query"
+    },
+    {
+      "from": "q_人类行为_r2_21",
+      "to": "sug_人类行为与社会环境_r2_q21_0",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_人类行为_r2_21",
+      "to": "sug_人类行为设计师-小周_r2_q21_1",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_人类行为_r2_21",
+      "to": "sug_人类行为与社会环境重点_r2_q21_2",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_人类行为_r2_21",
+      "to": "sug_人类行为与社会环境笔记_r2_q21_3",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_人类行为_r2_21",
+      "to": "sug_人类行为矫正教育漫画_r2_q21_4",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_人类行为_r2_21",
+      "to": "sug_人类行为与社会环境第三版_r2_q21_5",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_人类行为_r2_21",
+      "to": "sug_人类行为矫正教育_r2_q21_6",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_人类行为_r2_21",
+      "to": "sug_人类行为心理学_r2_q21_7",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_人类行为_r2_21",
+      "to": "sug_人类行为观察员_r2_q21_8",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "q_人类行为_r2_21",
+      "to": "sug_人类行为艺术_r2_q21_9",
+      "edge_type": "q_to_sug",
+      "strategy": "推荐词"
+    },
+    {
+      "from": "round_2",
+      "to": "step_comb_r2",
+      "edge_type": "round_to_step",
+      "strategy": "域内组词"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_如何反映_r2_0",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_如何人类_r2_1",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_如何双标_r2_2",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_如何行为_r2_3",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_如何反映人类_r2_4",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_如何反映双标_r2_5",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_如何反映行为_r2_6",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_如何人类双标_r2_7",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_如何人类行为_r2_8",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_如何双标行为_r2_9",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_如何反映人类双标_r2_10",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_如何反映人类行为_r2_11",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_如何反映双标行为_r2_12",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_如何人类双标行为_r2_13",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_如何反映人类双标行为_r2_14",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_如何猫咪_r2_15",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_如何表情包_r2_16",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_如何梗图_r2_17",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_如何猫咪表情包_r2_18",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_如何猫咪梗图_r2_19",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_如何表情包梗图_r2_20",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_如何猫咪表情包梗图_r2_21",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_制作反映_r2_22",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_制作人类_r2_23",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_制作双标_r2_24",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_制作行为_r2_25",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_制作反映人类_r2_26",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_制作反映双标_r2_27",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_制作反映行为_r2_28",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_制作人类双标_r2_29",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_制作人类行为_r2_30",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_制作双标行为_r2_31",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_制作反映人类双标_r2_32",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_制作反映人类行为_r2_33",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_制作反映双标行为_r2_34",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_制作人类双标行为_r2_35",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_制作反映人类双标行为_r2_36",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_制作猫咪_r2_37",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_制作表情包_r2_38",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_制作梗图_r2_39",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_制作猫咪表情包_r2_40",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_制作猫咪梗图_r2_41",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_制作表情包梗图_r2_42",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_制作猫咪表情包梗图_r2_43",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映猫咪_r2_44",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映表情包_r2_45",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映梗图_r2_46",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映猫咪表情包_r2_47",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映猫咪梗图_r2_48",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映表情包梗图_r2_49",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映猫咪表情包梗图_r2_50",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_人类猫咪_r2_51",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_人类表情包_r2_52",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_人类梗图_r2_53",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_人类猫咪表情包_r2_54",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_人类猫咪梗图_r2_55",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_人类表情包梗图_r2_56",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_人类猫咪表情包梗图_r2_57",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_双标猫咪_r2_58",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_双标表情包_r2_59",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_双标梗图_r2_60",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_双标猫咪表情包_r2_61",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_双标猫咪梗图_r2_62",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_双标表情包梗图_r2_63",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_双标猫咪表情包梗图_r2_64",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_行为猫咪_r2_65",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_行为表情包_r2_66",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_行为梗图_r2_67",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_行为猫咪表情包_r2_68",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_行为猫咪梗图_r2_69",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_行为表情包梗图_r2_70",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_行为猫咪表情包梗图_r2_71",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映人类猫咪_r2_72",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映人类表情包_r2_73",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映人类梗图_r2_74",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映人类猫咪表情包_r2_75",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映人类猫咪梗图_r2_76",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映人类表情包梗图_r2_77",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映人类猫咪表情包梗图_r2_78",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映双标猫咪_r2_79",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映双标表情包_r2_80",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映双标梗图_r2_81",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映双标猫咪表情包_r2_82",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映双标猫咪梗图_r2_83",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映双标表情包梗图_r2_84",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映双标猫咪表情包梗图_r2_85",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映行为猫咪_r2_86",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映行为表情包_r2_87",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映行为梗图_r2_88",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映行为猫咪表情包_r2_89",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映行为猫咪梗图_r2_90",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映行为表情包梗图_r2_91",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映行为猫咪表情包梗图_r2_92",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_人类双标猫咪_r2_93",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_人类双标表情包_r2_94",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_人类双标梗图_r2_95",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_人类双标猫咪表情包_r2_96",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_人类双标猫咪梗图_r2_97",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_人类双标表情包梗图_r2_98",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_人类双标猫咪表情包梗图_r2_99",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_人类行为猫咪_r2_100",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_人类行为表情包_r2_101",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_人类行为梗图_r2_102",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_人类行为猫咪表情包_r2_103",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_人类行为猫咪梗图_r2_104",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_人类行为表情包梗图_r2_105",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_人类行为猫咪表情包梗图_r2_106",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_双标行为猫咪_r2_107",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_双标行为表情包_r2_108",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_双标行为梗图_r2_109",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_双标行为猫咪表情包_r2_110",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_双标行为猫咪梗图_r2_111",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_双标行为表情包梗图_r2_112",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_双标行为猫咪表情包梗图_r2_113",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映人类双标猫咪_r2_114",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映人类双标表情包_r2_115",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映人类双标梗图_r2_116",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映人类双标猫咪表情包_r2_117",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映人类双标猫咪梗图_r2_118",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映人类双标表情包梗图_r2_119",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映人类双标猫咪表情包梗图_r2_120",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映人类行为猫咪_r2_121",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映人类行为表情包_r2_122",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映人类行为梗图_r2_123",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映人类行为猫咪表情包_r2_124",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映人类行为猫咪梗图_r2_125",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映人类行为表情包梗图_r2_126",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映人类行为猫咪表情包梗图_r2_127",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映双标行为猫咪_r2_128",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映双标行为表情包_r2_129",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映双标行为梗图_r2_130",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映双标行为猫咪表情包_r2_131",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映双标行为猫咪梗图_r2_132",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映双标行为表情包梗图_r2_133",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映双标行为猫咪表情包梗图_r2_134",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_人类双标行为猫咪_r2_135",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_人类双标行为表情包_r2_136",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_人类双标行为梗图_r2_137",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_人类双标行为猫咪表情包_r2_138",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_人类双标行为猫咪梗图_r2_139",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_人类双标行为表情包梗图_r2_140",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_人类双标行为猫咪表情包梗图_r2_141",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映人类双标行为猫咪_r2_142",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映人类双标行为表情包_r2_143",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映人类双标行为梗图_r2_144",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映人类双标行为猫咪表情包_r2_145",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映人类双标行为猫咪梗图_r2_146",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映人类双标行为表情包梗图_r2_147",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_comb_r2",
+      "to": "comb_反映人类双标行为猫咪表情包梗图_r2_148",
+      "edge_type": "step_to_comb",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "round_2",
+      "to": "step_search_r2",
+      "edge_type": "round_to_step",
+      "strategy": "搜索"
+    },
+    {
+      "from": "step_search_r2",
+      "to": "search_制作表情包教程_r2_0",
+      "edge_type": "step_to_search_word",
+      "strategy": "搜索词"
+    },
+    {
+      "from": "search_制作表情包教程_r2_0",
+      "to": "post_66f9046a000000002a033202_0_0",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_制作表情包教程_r2_0",
+      "to": "post_668e06cd00000000250145fe_0_1",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_制作表情包教程_r2_0",
+      "to": "post_67dc1a58000000001d0232da_0_2",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_制作表情包教程_r2_0",
+      "to": "post_68282bbd000000002001f349_0_3",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_制作表情包教程_r2_0",
+      "to": "post_66ea4e30000000001e01964b_0_4",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_制作表情包教程_r2_0",
+      "to": "post_6690ebe500000000250153e5_0_5",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_制作表情包教程_r2_0",
+      "to": "post_671d9a72000000001402e50f_0_6",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_制作表情包教程_r2_0",
+      "to": "post_668f5479000000000d00ebc2_0_7",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_制作表情包教程_r2_0",
+      "to": "post_680054bc000000001d014b19_0_8",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_制作表情包教程_r2_0",
+      "to": "post_690818e70000000003011fa5_0_9",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "step_search_r2",
+      "to": "search_怼双标的人表情包_r2_1",
+      "edge_type": "step_to_search_word",
+      "strategy": "搜索词"
+    },
+    {
+      "from": "search_怼双标的人表情包_r2_1",
+      "to": "post_66af92ad0000000005038228_1_0",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_怼双标的人表情包_r2_1",
+      "to": "post_68597fed000000001202d443_1_1",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_怼双标的人表情包_r2_1",
+      "to": "post_6541e7fb000000002303bfea_1_2",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_怼双标的人表情包_r2_1",
+      "to": "post_67bb4d7d000000002903d2fb_1_3",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_怼双标的人表情包_r2_1",
+      "to": "post_6791c3e600000000170392b9_1_4",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_怼双标的人表情包_r2_1",
+      "to": "post_69080c43000000000402bb4b_1_5",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_怼双标的人表情包_r2_1",
+      "to": "post_66791a5f000000001c027fab_1_6",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_怼双标的人表情包_r2_1",
+      "to": "post_66e0e9b900000000270026f2_1_7",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_怼双标的人表情包_r2_1",
+      "to": "post_67db7618000000001c008ddb_1_8",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_怼双标的人表情包_r2_1",
+      "to": "post_66caccf2000000001f03bcee_1_9",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "step_search_r2",
+      "to": "search_可爱又双标的表情包_r2_2",
+      "edge_type": "step_to_search_word",
+      "strategy": "搜索词"
+    },
+    {
+      "from": "search_可爱又双标的表情包_r2_2",
+      "to": "post_690ec7070000000004022819_2_0",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_可爱又双标的表情包_r2_2",
+      "to": "post_66877a00000000000a0073d7_2_1",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_可爱又双标的表情包_r2_2",
+      "to": "post_68ae7e48000000001c004610_2_2",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_可爱又双标的表情包_r2_2",
+      "to": "post_6697b8310000000025003164_2_3",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_可爱又双标的表情包_r2_2",
+      "to": "post_68626478000000000d019251_2_4",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_可爱又双标的表情包_r2_2",
+      "to": "post_68dde20600000000070173ef_2_5",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_可爱又双标的表情包_r2_2",
+      "to": "post_67dd3945000000001d02d900_2_6",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_可爱又双标的表情包_r2_2",
+      "to": "post_6907666b0000000004022dbb_2_7",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_可爱又双标的表情包_r2_2",
+      "to": "post_68a43430000000001d0027d6_2_8",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_可爱又双标的表情包_r2_2",
+      "to": "post_67d2e30d000000000603eb03_2_9",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "step_search_r2",
+      "to": "search_梗图描改教程_r2_3",
+      "edge_type": "step_to_search_word",
+      "strategy": "搜索词"
+    },
+    {
+      "from": "search_梗图描改教程_r2_3",
+      "to": "post_68ef8753000000000301d581_3_0",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_梗图描改教程_r2_3",
+      "to": "post_6882582c000000000d018245_3_1",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_梗图描改教程_r2_3",
+      "to": "post_689937da0000000022023e1e_3_2",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_梗图描改教程_r2_3",
+      "to": "post_68fdde910000000003018b60_3_3",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_梗图描改教程_r2_3",
+      "to": "post_63cfabdd000000001b0173d5_3_4",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_梗图描改教程_r2_3",
+      "to": "post_62e4f79a000000000e0308ed_3_5",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_梗图描改教程_r2_3",
+      "to": "post_68429814000000002001dfc8_3_6",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_梗图描改教程_r2_3",
+      "to": "post_68f485a60000000003010182_3_7",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_梗图描改教程_r2_3",
+      "to": "post_682b5cc3000000000f031bf9_3_8",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_梗图描改教程_r2_3",
+      "to": "post_681c78d50000000020028a5c_3_9",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "step_search_r2",
+      "to": "search_梗图meme原创_r2_4",
+      "edge_type": "step_to_search_word",
+      "strategy": "搜索词"
+    },
+    {
+      "from": "search_梗图meme原创_r2_4",
+      "to": "post_68e06587000000000401467d_4_0",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_梗图meme原创_r2_4",
+      "to": "post_690a02720000000005002039_4_1",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_梗图meme原创_r2_4",
+      "to": "post_68b27225000000001b03e3a4_4_2",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_梗图meme原创_r2_4",
+      "to": "post_677e6b540000000001000781_4_3",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_梗图meme原创_r2_4",
+      "to": "post_6640beca000000001e023a10_4_4",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_梗图meme原创_r2_4",
+      "to": "post_67a38120000000001801354d_4_5",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_梗图meme原创_r2_4",
+      "to": "post_66f6d0df000000002c029dc7_4_6",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_梗图meme原创_r2_4",
+      "to": "post_686cfdf60000000023005633_4_7",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_梗图meme原创_r2_4",
+      "to": "post_674db30c00000000070278f1_4_8",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_梗图meme原创_r2_4",
+      "to": "post_6913214e0000000003034f7f_4_9",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "step_search_r2",
+      "to": "search_猫咪表情包梗图搞笑_r2_5",
+      "edge_type": "step_to_search_word",
+      "strategy": "搜索词"
+    },
+    {
+      "from": "search_猫咪表情包梗图搞笑_r2_5",
+      "to": "post_67d2310a000000001d026c02_5_0",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_猫咪表情包梗图搞笑_r2_5",
+      "to": "post_68d8bddf000000000e031155_5_1",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_猫咪表情包梗图搞笑_r2_5",
+      "to": "post_6710c5a7000000001402dc9f_5_2",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_猫咪表情包梗图搞笑_r2_5",
+      "to": "post_6731806d000000001b02cf7f_5_3",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_猫咪表情包梗图搞笑_r2_5",
+      "to": "post_66505e0c000000001401add3_5_4",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_猫咪表情包梗图搞笑_r2_5",
+      "to": "post_6629f62a00000000030233d9_5_5",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_猫咪表情包梗图搞笑_r2_5",
+      "to": "post_68e71e340000000007002e56_5_6",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_猫咪表情包梗图搞笑_r2_5",
+      "to": "post_67cbe638000000001203e839_5_7",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_猫咪表情包梗图搞笑_r2_5",
+      "to": "post_6826f7b7000000002202a171_5_8",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_猫咪表情包梗图搞笑_r2_5",
+      "to": "post_666d59f2000000000e032b89_5_9",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "step_search_r2",
+      "to": "search_猫咪表情包制作_r2_6",
+      "edge_type": "step_to_search_word",
+      "strategy": "搜索词"
+    },
+    {
+      "from": "search_猫咪表情包制作_r2_6",
+      "to": "post_660581800000000012023abc_6_0",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_猫咪表情包制作_r2_6",
+      "to": "post_678c8258000000001b00b500_6_1",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_猫咪表情包制作_r2_6",
+      "to": "post_67f60c23000000001c03e8b0_6_2",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_猫咪表情包制作_r2_6",
+      "to": "post_6618ef8a000000001b00815c_6_3",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_猫咪表情包制作_r2_6",
+      "to": "post_68650c0e000000002400ec34_6_4",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_猫咪表情包制作_r2_6",
+      "to": "post_67541e26000000000800553c_6_5",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_猫咪表情包制作_r2_6",
+      "to": "post_68ee4f9400000000050318ba_6_6",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_猫咪表情包制作_r2_6",
+      "to": "post_6803ae1a000000001c036da7_6_7",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_猫咪表情包制作_r2_6",
+      "to": "post_66209a4c00000000040180ad_6_8",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "search_猫咪表情包制作_r2_6",
+      "to": "post_618ff44f0000000001029e17_6_9",
+      "edge_type": "search_word_to_post",
+      "strategy": "搜索结果"
+    },
+    {
+      "from": "round_2",
+      "to": "step_next_round_r2",
+      "edge_type": "round_to_step",
+      "strategy": "构建下一轮"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_制作猫咪表情包梗图_r2_0",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_制作猫咪表情包_r2_1",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_制作猫咪梗图_r2_2",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_制作表情包梗图_r2_3",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_制作表情包_r2_4",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_制作反映人类双标行为_r2_5",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_制作反映人类双标_r2_6",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_制作人类双标_r2_7",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_制作人类双标行为_r2_8",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_制作反映双标_r2_9",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_制作反映双标行为_r2_10",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_制作双标行为_r2_11",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_制作反映人类行为_r2_12",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_反映人类双标猫咪表情包_r2_13",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_反映双标猫咪梗图_r2_14",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_如何猫咪表情包梗图_r2_15",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_反映人类双标行为表情包梗图_r2_16",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_如何猫咪表情包_r2_17",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_如何猫咪梗图_r2_18",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_反映双标行为表情包梗图_r2_19",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_反映行为猫咪梗图_r2_20",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_如何表情包梗图_r2_21",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_反映行为表情包梗图_r2_22",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_如何反映人类双标行为_r2_23",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_如何反映人类双标_r2_24",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_如何反映双标_r2_25",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_如何反映双标行为_r2_26",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_如何反映人类行为_r2_27",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_如何反映_r2_28",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_如何梗图_r2_29",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_如何人类双标行为_r2_30",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_如何反映行为_r2_31",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_如何人类双标_r2_32",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_反映人类双标梗图_r2_33",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_反映人类双标行为梗图_r2_34",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_反映双标梗图_r2_35",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_人类双标梗图_r2_36",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_人类双标行为梗图_r2_37",
+      "edge_type": "step_to_next_round",
+      "strategy": "域内组合"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_猫咪表情包制作_r2_38",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_梗图描改教程_r2_39",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_可爱又双标的表情包_r2_40",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_怼双标的人表情包_r2_41",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_梗图meme原创_r2_42",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_猫咪表情包梗图搞笑_r2_43",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_猫咪表情包图片_r2_44",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_猫咪表情包图片_r2_45",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_讽刺双标的图片_r2_46",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_讽刺双标的图片_r2_47",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_双标表情包图片_r2_48",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_猫咪表情包_r2_49",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_梗图描改过程_r2_50",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_猫咪表情包视频_r2_51",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_猫咪搞笑表情包_r2_52",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_反映人类双标行为的图片_r2_53",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_猫咪双标行为_r2_54",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_梗图素材抽象_r2_55",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_猫咪咪表情包图片_r2_56",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_猫咪表情包动图_r2_57",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_表情包梗图动图_r2_58",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_梗图素材表情包_r2_59",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_猫咪梗图素材_r2_60",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_猫咪梗图模板_r2_61",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_表情包梗图学习_r2_62",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_猫咪表情包配文_r2_63",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_梗图meme素材_r2_64",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_猫咪表情包可爱_r2_65",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_表情包梗图抽象_r2_66",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_猫meme表情包动图_r2_67",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_猫咪梗图骂人_r2_68",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_梗图模版表情包_r2_69",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_猫咪梗图伤感_r2_70",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_猫咪双标行为搞笑视频_r2_71",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_猫咪梗图拍照_r2_72",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_双标搞笑图片_r2_73",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_猫咪梗图英文_r2_74",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_梗图描改模板_r2_75",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_双标梗_r2_76",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_暗示双标的图片_r2_77",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_meme梗图分享_r2_78",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_双标行为举例_r2_79",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_双标_r2_80",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    },
+    {
+      "from": "step_next_round_r2",
+      "to": "next_round_表情包图片大全素材_r2_81",
+      "edge_type": "step_to_next_round",
+      "strategy": "SUG"
+    }
+  ],
+  "iterations": {
+    "0": [
+      "root_o",
+      "round_0"
+    ],
+    "1": [
+      "step_seg_r0"
+    ],
+    "2": [
+      "segment_0_r0",
+      "segment_1_r0",
+      "segment_2_r0",
+      "segment_3_r0"
+    ],
+    "3": [
+      "word_如何_seg0_0",
+      "word_制作_seg1_0",
+      "word_反映_seg2_0",
+      "word_人类_seg2_1",
+      "word_双标_seg2_2",
+      "word_行为_seg2_3",
+      "word_猫咪_seg3_0",
+      "word_表情包_seg3_1",
+      "word_梗图_seg3_2"
+    ],
+    "10": [
+      "round_1",
+      "step_sug_r1",
+      "step_comb_r1",
+      "step_search_r1",
+      "step_next_round_r1"
+    ],
+    "12": [
+      "q_如何_r1_0",
+      "q_制作_r1_1",
+      "q_反映_r1_2",
+      "q_人类_r1_3",
+      "q_双标_r1_4",
+      "q_行为_r1_5",
+      "q_猫咪_r1_6",
+      "q_表情包_r1_7",
+      "q_梗图_r1_8",
+      "comb_反映人类_r1_0",
+      "comb_反映双标_r1_1",
+      "comb_反映行为_r1_2",
+      "comb_人类双标_r1_3",
+      "comb_人类行为_r1_4",
+      "comb_双标行为_r1_5",
+      "comb_反映人类双标_r1_6",
+      "comb_反映人类行为_r1_7",
+      "comb_反映双标行为_r1_8",
+      "comb_人类双标行为_r1_9",
+      "comb_反映人类双标行为_r1_10",
+      "comb_猫咪表情包_r1_11",
+      "comb_猫咪梗图_r1_12",
+      "comb_表情包梗图_r1_13",
+      "comb_猫咪表情包梗图_r1_14",
+      "search_制作表情包_r1_0",
+      "search_表情包怎么制作_r1_1",
+      "next_round_猫咪表情包梗图_r1_0",
+      "next_round_猫咪表情包_r1_1",
+      "next_round_猫咪梗图_r1_2",
+      "next_round_表情包梗图_r1_3",
+      "next_round_反映人类双标_r1_4",
+      "next_round_反映人类双标行为_r1_5",
+      "next_round_反映双标_r1_6",
+      "next_round_人类双标_r1_7",
+      "next_round_人类双标行为_r1_8",
+      "next_round_双标行为_r1_9",
+      "next_round_反映双标行为_r1_10",
+      "next_round_反映人类_r1_11",
+      "next_round_反映人类行为_r1_12",
+      "next_round_人类行为_r1_13",
+      "next_round_制作表情包_r1_14",
+      "next_round_表情包怎么制作_r1_15",
+      "next_round_双标表情包_r1_16",
+      "next_round_梗图素材_r1_17",
+      "next_round_梗图模版_r1_18",
+      "next_round_梗图描改_r1_19",
+      "next_round_表情包简笔画_r1_20",
+      "next_round_双标图片_r1_21",
+      "next_round_表情包图片大全_r1_22",
+      "next_round_梗图meme_r1_23",
+      "next_round_梗图分享_r1_24"
+    ],
+    "13": [
+      "sug_如何快速减肥_r1_q0_0",
+      "sug_如何培养男人主动给你花钱_r1_q0_1",
+      "sug_如何快速挣到钱_r1_q0_2",
+      "sug_如何和女生聊天找话题_r1_q0_3",
+      "sug_如何快速入睡_r1_q0_4",
+      "sug_如何让男生持续上头_r1_q0_5",
+      "sug_如何逼自己自律学习_r1_q0_6",
+      "sug_如何连接别人家的加密wifi_r1_q0_7",
+      "sug_如何养好头发_r1_q0_8",
+      "sug_如何治疗早泻时间短_r1_q0_9",
+      "sug_制作ppt_r1_q1_0",
+      "sug_制作表情包_r1_q1_1",
+      "sug_制作冰糖葫芦_r1_q1_2",
+      "sug_制作ppt的ai软件_r1_q1_3",
+      "sug_制作视频_r1_q1_4",
+      "sug_制作美食_r1_q1_5",
+      "sug_制作简历_r1_q1_6",
+      "sug_制作饮品_r1_q1_7",
+      "sug_制作小房子_r1_q1_8",
+      "sug_制作表格_r1_q1_9",
+      "sug_反映问题还是反应问题_r1_q2_0",
+      "sug_反映和反应的区别_r1_q2_1",
+      "sug_反映是什么意思_r1_q2_2",
+      "sug_反映者_r1_q2_3",
+      "sug_反应力小游戏_r1_q2_4",
+      "sug_反应蛋白高是什么意思_r1_q2_5",
+      "sug_反映拼音_r1_q2_6",
+      "sug_反映财务状况的会计要素_r1_q2_7",
+      "sug_反映的英语_r1_q2_8",
+      "sug_反映事物间的互补关系_r1_q2_9",
+      "sug_人类一败涂地_r1_q3_0",
+      "sug_人类狗窝_r1_q3_1",
+      "sug_人类幼崽陪伴指南_r1_q3_2",
+      "sug_人类用沙想捏出梦里通天塔_r1_q3_3",
+      "sug_人类幼仔_r1_q3_4",
+      "sug_人类简史_r1_q3_5",
+      "sug_人类进化史_r1_q3_6",
+      "sug_人类跌落梦境_r1_q3_7",
+      "sug_人类群星闪耀时_r1_q3_8",
+      "sug_人类高质量男姓_r1_q3_9",
+      "sug_双标是什么意思_r1_q4_0",
+      "sug_讽刺双标的文案_r1_q4_1",
+      "sug_双标的人是什么心理_r1_q4_2",
+      "sug_双标文案_r1_q4_3",
+      "sug_双标高爆卡点伴奏_r1_q4_4",
+      "sug_双标信用卡_r1_q4_5",
+      "sug_双椒兔做法_r1_q4_6",
+      "sug_双标图片_r1_q4_7",
+      "sug_双标表情包_r1_q4_8",
+      "sug_双标的人_r1_q4_9",
+      "sug_行为心理学_r1_q5_0",
+      "sug_行为基础_r1_q5_1",
+      "sug_行为规范手抄报_r1_q5_2",
+      "sug_行为习惯手抄报_r1_q5_3",
+      "sug_行为艺术_r1_q5_4",
+      "sug_行为决定关系而非关系决定行为_r1_q5_5",
+      "sug_行为违反腾讯用户协议_r1_q5_6",
+      "sug_行为认知疗法_r1_q5_7",
+      "sug_行为经济学_r1_q5_8",
+      "sug_行为规范家_r1_q5_9",
+      "sug_猫咪领养免费领养_r1_q6_0",
+      "sug_猫咪叫声吸引小猫_r1_q6_1",
+      "sug_猫咪呕吐_r1_q6_2",
+      "sug_猫咪头像_r1_q6_3",
+      "sug_猫咪取名_r1_q6_4",
+      "sug_猫咪品种_r1_q6_5",
+      "sug_猫咪搞笑视频_r1_q6_6",
+      "sug_猫咪叫声_r1_q6_7",
+      "sug_猫咪驱虫药推荐_r1_q6_8",
+      "sug_猫咪黑下巴怎么处理_r1_q6_9",
+      "sug_表情包抽象_r1_q7_0",
+      "sug_表情包怎么制作_r1_q7_1",
+      "sug_表情包可爱_r1_q7_2",
+      "sug_表情包图片大全_r1_q7_3",
+      "sug_表情包搞笑配文_r1_q7_4",
+      "sug_表情包发给女朋友_r1_q7_5",
+      "sug_表情包简笔画_r1_q7_6",
+      "sug_表情包模板_r1_q7_7",
+      "sug_表情包发给男朋友_r1_q7_8",
+      "sug_表情包制作赚钱_r1_q7_9",
+      "sug_梗图素材_r1_q8_0",
+      "sug_梗图搞笑_r1_q8_1",
+      "sug_梗图精神状态_r1_q8_2",
+      "sug_梗图meme_r1_q8_3",
+      "sug_梗图双人_r1_q8_4",
+      "sug_梗图抽象_r1_q8_5",
+      "sug_梗图描改_r1_q8_6",
+      "sug_梗图模版_r1_q8_7",
+      "sug_梗图分享_r1_q8_8",
+      "sug_梗图大全_r1_q8_9",
+      "post_66f9046a000000002a033202_0_0",
+      "post_668e06cd00000000250145fe_0_1",
+      "post_68282bbd000000002001f349_0_2",
+      "post_67d1b6c9000000002802a550_0_3",
+      "post_68ef01ad000000000302f388_0_4",
+      "post_66ea4e30000000001e01964b_0_5",
+      "post_67c72549000000000602a376_0_6",
+      "post_6876f73f0000000011002f0c_0_7",
+      "post_6613c01f000000001b00820c_0_8",
+      "post_61b81195000000002103b7ab_0_9"
+    ],
+    "20": [
+      "round_2",
+      "step_sug_r2",
+      "step_comb_r2",
+      "step_search_r2",
+      "step_next_round_r2"
+    ],
+    "22": [
+      "q_制作表情包_r2_0",
+      "q_表情包怎么制作_r2_1",
+      "q_双标表情包_r2_2",
+      "q_梗图素材_r2_3",
+      "q_梗图模版_r2_4",
+      "q_梗图描改_r2_5",
+      "q_表情包简笔画_r2_6",
+      "q_双标图片_r2_7",
+      "q_表情包图片大全_r2_8",
+      "q_梗图meme_r2_9",
+      "q_梗图分享_r2_10",
+      "q_猫咪表情包梗图_r2_11",
+      "q_猫咪表情包_r2_12",
+      "q_猫咪梗图_r2_13",
+      "q_表情包梗图_r2_14",
+      "q_反映人类双标_r2_15",
+      "q_反映人类双标行为_r2_16",
+      "q_人类双标行为_r2_17",
+      "q_双标行为_r2_18",
+      "q_反映人类_r2_19",
+      "q_反映人类行为_r2_20",
+      "q_人类行为_r2_21",
+      "comb_如何反映_r2_0",
+      "comb_如何人类_r2_1",
+      "comb_如何双标_r2_2",
+      "comb_如何行为_r2_3",
+      "comb_如何反映人类_r2_4",
+      "comb_如何反映双标_r2_5",
+      "comb_如何反映行为_r2_6",
+      "comb_如何人类双标_r2_7",
+      "comb_如何人类行为_r2_8",
+      "comb_如何双标行为_r2_9",
+      "comb_如何反映人类双标_r2_10",
+      "comb_如何反映人类行为_r2_11",
+      "comb_如何反映双标行为_r2_12",
+      "comb_如何人类双标行为_r2_13",
+      "comb_如何反映人类双标行为_r2_14",
+      "comb_如何猫咪_r2_15",
+      "comb_如何表情包_r2_16",
+      "comb_如何梗图_r2_17",
+      "comb_如何猫咪表情包_r2_18",
+      "comb_如何猫咪梗图_r2_19",
+      "comb_如何表情包梗图_r2_20",
+      "comb_如何猫咪表情包梗图_r2_21",
+      "comb_制作反映_r2_22",
+      "comb_制作人类_r2_23",
+      "comb_制作双标_r2_24",
+      "comb_制作行为_r2_25",
+      "comb_制作反映人类_r2_26",
+      "comb_制作反映双标_r2_27",
+      "comb_制作反映行为_r2_28",
+      "comb_制作人类双标_r2_29",
+      "comb_制作人类行为_r2_30",
+      "comb_制作双标行为_r2_31",
+      "comb_制作反映人类双标_r2_32",
+      "comb_制作反映人类行为_r2_33",
+      "comb_制作反映双标行为_r2_34",
+      "comb_制作人类双标行为_r2_35",
+      "comb_制作反映人类双标行为_r2_36",
+      "comb_制作猫咪_r2_37",
+      "comb_制作表情包_r2_38",
+      "comb_制作梗图_r2_39",
+      "comb_制作猫咪表情包_r2_40",
+      "comb_制作猫咪梗图_r2_41",
+      "comb_制作表情包梗图_r2_42",
+      "comb_制作猫咪表情包梗图_r2_43",
+      "comb_反映猫咪_r2_44",
+      "comb_反映表情包_r2_45",
+      "comb_反映梗图_r2_46",
+      "comb_反映猫咪表情包_r2_47",
+      "comb_反映猫咪梗图_r2_48",
+      "comb_反映表情包梗图_r2_49",
+      "comb_反映猫咪表情包梗图_r2_50",
+      "comb_人类猫咪_r2_51",
+      "comb_人类表情包_r2_52",
+      "comb_人类梗图_r2_53",
+      "comb_人类猫咪表情包_r2_54",
+      "comb_人类猫咪梗图_r2_55",
+      "comb_人类表情包梗图_r2_56",
+      "comb_人类猫咪表情包梗图_r2_57",
+      "comb_双标猫咪_r2_58",
+      "comb_双标表情包_r2_59",
+      "comb_双标梗图_r2_60",
+      "comb_双标猫咪表情包_r2_61",
+      "comb_双标猫咪梗图_r2_62",
+      "comb_双标表情包梗图_r2_63",
+      "comb_双标猫咪表情包梗图_r2_64",
+      "comb_行为猫咪_r2_65",
+      "comb_行为表情包_r2_66",
+      "comb_行为梗图_r2_67",
+      "comb_行为猫咪表情包_r2_68",
+      "comb_行为猫咪梗图_r2_69",
+      "comb_行为表情包梗图_r2_70",
+      "comb_行为猫咪表情包梗图_r2_71",
+      "comb_反映人类猫咪_r2_72",
+      "comb_反映人类表情包_r2_73",
+      "comb_反映人类梗图_r2_74",
+      "comb_反映人类猫咪表情包_r2_75",
+      "comb_反映人类猫咪梗图_r2_76",
+      "comb_反映人类表情包梗图_r2_77",
+      "comb_反映人类猫咪表情包梗图_r2_78",
+      "comb_反映双标猫咪_r2_79",
+      "comb_反映双标表情包_r2_80",
+      "comb_反映双标梗图_r2_81",
+      "comb_反映双标猫咪表情包_r2_82",
+      "comb_反映双标猫咪梗图_r2_83",
+      "comb_反映双标表情包梗图_r2_84",
+      "comb_反映双标猫咪表情包梗图_r2_85",
+      "comb_反映行为猫咪_r2_86",
+      "comb_反映行为表情包_r2_87",
+      "comb_反映行为梗图_r2_88",
+      "comb_反映行为猫咪表情包_r2_89",
+      "comb_反映行为猫咪梗图_r2_90",
+      "comb_反映行为表情包梗图_r2_91",
+      "comb_反映行为猫咪表情包梗图_r2_92",
+      "comb_人类双标猫咪_r2_93",
+      "comb_人类双标表情包_r2_94",
+      "comb_人类双标梗图_r2_95",
+      "comb_人类双标猫咪表情包_r2_96",
+      "comb_人类双标猫咪梗图_r2_97",
+      "comb_人类双标表情包梗图_r2_98",
+      "comb_人类双标猫咪表情包梗图_r2_99",
+      "comb_人类行为猫咪_r2_100",
+      "comb_人类行为表情包_r2_101",
+      "comb_人类行为梗图_r2_102",
+      "comb_人类行为猫咪表情包_r2_103",
+      "comb_人类行为猫咪梗图_r2_104",
+      "comb_人类行为表情包梗图_r2_105",
+      "comb_人类行为猫咪表情包梗图_r2_106",
+      "comb_双标行为猫咪_r2_107",
+      "comb_双标行为表情包_r2_108",
+      "comb_双标行为梗图_r2_109",
+      "comb_双标行为猫咪表情包_r2_110",
+      "comb_双标行为猫咪梗图_r2_111",
+      "comb_双标行为表情包梗图_r2_112",
+      "comb_双标行为猫咪表情包梗图_r2_113",
+      "comb_反映人类双标猫咪_r2_114",
+      "comb_反映人类双标表情包_r2_115",
+      "comb_反映人类双标梗图_r2_116",
+      "comb_反映人类双标猫咪表情包_r2_117",
+      "comb_反映人类双标猫咪梗图_r2_118",
+      "comb_反映人类双标表情包梗图_r2_119",
+      "comb_反映人类双标猫咪表情包梗图_r2_120",
+      "comb_反映人类行为猫咪_r2_121",
+      "comb_反映人类行为表情包_r2_122",
+      "comb_反映人类行为梗图_r2_123",
+      "comb_反映人类行为猫咪表情包_r2_124",
+      "comb_反映人类行为猫咪梗图_r2_125",
+      "comb_反映人类行为表情包梗图_r2_126",
+      "comb_反映人类行为猫咪表情包梗图_r2_127",
+      "comb_反映双标行为猫咪_r2_128",
+      "comb_反映双标行为表情包_r2_129",
+      "comb_反映双标行为梗图_r2_130",
+      "comb_反映双标行为猫咪表情包_r2_131",
+      "comb_反映双标行为猫咪梗图_r2_132",
+      "comb_反映双标行为表情包梗图_r2_133",
+      "comb_反映双标行为猫咪表情包梗图_r2_134",
+      "comb_人类双标行为猫咪_r2_135",
+      "comb_人类双标行为表情包_r2_136",
+      "comb_人类双标行为梗图_r2_137",
+      "comb_人类双标行为猫咪表情包_r2_138",
+      "comb_人类双标行为猫咪梗图_r2_139",
+      "comb_人类双标行为表情包梗图_r2_140",
+      "comb_人类双标行为猫咪表情包梗图_r2_141",
+      "comb_反映人类双标行为猫咪_r2_142",
+      "comb_反映人类双标行为表情包_r2_143",
+      "comb_反映人类双标行为梗图_r2_144",
+      "comb_反映人类双标行为猫咪表情包_r2_145",
+      "comb_反映人类双标行为猫咪梗图_r2_146",
+      "comb_反映人类双标行为表情包梗图_r2_147",
+      "comb_反映人类双标行为猫咪表情包梗图_r2_148",
+      "search_制作表情包教程_r2_0",
+      "search_怼双标的人表情包_r2_1",
+      "search_可爱又双标的表情包_r2_2",
+      "search_梗图描改教程_r2_3",
+      "search_梗图meme原创_r2_4",
+      "search_猫咪表情包梗图搞笑_r2_5",
+      "search_猫咪表情包制作_r2_6",
+      "next_round_制作猫咪表情包梗图_r2_0",
+      "next_round_制作猫咪表情包_r2_1",
+      "next_round_制作猫咪梗图_r2_2",
+      "next_round_制作表情包梗图_r2_3",
+      "next_round_制作表情包_r2_4",
+      "next_round_制作反映人类双标行为_r2_5",
+      "next_round_制作反映人类双标_r2_6",
+      "next_round_制作人类双标_r2_7",
+      "next_round_制作人类双标行为_r2_8",
+      "next_round_制作反映双标_r2_9",
+      "next_round_制作反映双标行为_r2_10",
+      "next_round_制作双标行为_r2_11",
+      "next_round_制作反映人类行为_r2_12",
+      "next_round_反映人类双标猫咪表情包_r2_13",
+      "next_round_反映双标猫咪梗图_r2_14",
+      "next_round_如何猫咪表情包梗图_r2_15",
+      "next_round_反映人类双标行为表情包梗图_r2_16",
+      "next_round_如何猫咪表情包_r2_17",
+      "next_round_如何猫咪梗图_r2_18",
+      "next_round_反映双标行为表情包梗图_r2_19",
+      "next_round_反映行为猫咪梗图_r2_20",
+      "next_round_如何表情包梗图_r2_21",
+      "next_round_反映行为表情包梗图_r2_22",
+      "next_round_如何反映人类双标行为_r2_23",
+      "next_round_如何反映人类双标_r2_24",
+      "next_round_如何反映双标_r2_25",
+      "next_round_如何反映双标行为_r2_26",
+      "next_round_如何反映人类行为_r2_27",
+      "next_round_如何反映_r2_28",
+      "next_round_如何梗图_r2_29",
+      "next_round_如何人类双标行为_r2_30",
+      "next_round_如何反映行为_r2_31",
+      "next_round_如何人类双标_r2_32",
+      "next_round_反映人类双标梗图_r2_33",
+      "next_round_反映人类双标行为梗图_r2_34",
+      "next_round_反映双标梗图_r2_35",
+      "next_round_人类双标梗图_r2_36",
+      "next_round_人类双标行为梗图_r2_37",
+      "next_round_猫咪表情包制作_r2_38",
+      "next_round_梗图描改教程_r2_39",
+      "next_round_可爱又双标的表情包_r2_40",
+      "next_round_怼双标的人表情包_r2_41",
+      "next_round_梗图meme原创_r2_42",
+      "next_round_猫咪表情包梗图搞笑_r2_43",
+      "next_round_猫咪表情包图片_r2_44",
+      "next_round_猫咪表情包图片_r2_45",
+      "next_round_讽刺双标的图片_r2_46",
+      "next_round_讽刺双标的图片_r2_47",
+      "next_round_双标表情包图片_r2_48",
+      "next_round_猫咪表情包_r2_49",
+      "next_round_梗图描改过程_r2_50",
+      "next_round_猫咪表情包视频_r2_51",
+      "next_round_猫咪搞笑表情包_r2_52",
+      "next_round_反映人类双标行为的图片_r2_53",
+      "next_round_猫咪双标行为_r2_54",
+      "next_round_梗图素材抽象_r2_55",
+      "next_round_猫咪咪表情包图片_r2_56",
+      "next_round_猫咪表情包动图_r2_57",
+      "next_round_表情包梗图动图_r2_58",
+      "next_round_梗图素材表情包_r2_59",
+      "next_round_猫咪梗图素材_r2_60",
+      "next_round_猫咪梗图模板_r2_61",
+      "next_round_表情包梗图学习_r2_62",
+      "next_round_猫咪表情包配文_r2_63",
+      "next_round_梗图meme素材_r2_64",
+      "next_round_猫咪表情包可爱_r2_65",
+      "next_round_表情包梗图抽象_r2_66",
+      "next_round_猫meme表情包动图_r2_67",
+      "next_round_猫咪梗图骂人_r2_68",
+      "next_round_梗图模版表情包_r2_69",
+      "next_round_猫咪梗图伤感_r2_70",
+      "next_round_猫咪双标行为搞笑视频_r2_71",
+      "next_round_猫咪梗图拍照_r2_72",
+      "next_round_双标搞笑图片_r2_73",
+      "next_round_猫咪梗图英文_r2_74",
+      "next_round_梗图描改模板_r2_75",
+      "next_round_双标梗_r2_76",
+      "next_round_暗示双标的图片_r2_77",
+      "next_round_meme梗图分享_r2_78",
+      "next_round_双标行为举例_r2_79",
+      "next_round_双标_r2_80",
+      "next_round_表情包图片大全素材_r2_81"
+    ],
+    "23": [
+      "sug_制作表情包用什么软件_r2_q0_0",
+      "sug_制作表情包微信_r2_q0_1",
+      "sug_制作表情包怎么赚钱_r2_q0_2",
+      "sug_制作表情包教程_r2_q0_3",
+      "sug_制作表情包软件_r2_q0_4",
+      "sug_制作表情包gif_r2_q0_5",
+      "sug_制作表情包动图_r2_q0_6",
+      "sug_视频制作表情包_r2_q0_7",
+      "sug_美图秀秀制作表情包图片_r2_q0_8",
+      "sug_制作表情包文字_r2_q0_9",
+      "sug_表情包怎么制作gif_r2_q1_0",
+      "sug_表情包怎么制作动态_r2_q1_1",
+      "sug_表情包怎么制作微信_r2_q1_2",
+      "sug_表情包怎么制作文字_r2_q1_3",
+      "sug_小红书表情包怎么制作的_r2_q1_4",
+      "sug_原创表情包怎么制作_r2_q1_5",
+      "sug_表情包怎么制作视频_r2_q1_6",
+      "sug_表情包怎么制作动态图_r2_q1_7",
+      "sug_表情包怎么制作动图_r2_q1_8",
+      "sug_制作表情包怎么赚钱_r2_q1_9",
+      "sug_双标表情包内涵_r2_q2_0",
+      "sug_双标表情包图片_r2_q2_1",
+      "sug_怼双标的人表情包_r2_q2_2",
+      "sug_可爱又双标的表情包_r2_q2_3",
+      "sug_讽刺双标的图片_r2_q2_4",
+      "sug_双标梗图模板_r2_q2_5",
+      "sug_讽刺双标的人的文案_r2_q2_6",
+      "sug_很双标的朋友圈文案_r2_q2_7",
+      "sug_这可是要砍头的表情包_r2_q2_8",
+      "sug_双标的人怎么怼_r2_q2_9",
+      "sug_梗图素材模板_r2_q3_0",
+      "sug_梗图素材抽象_r2_q3_1",
+      "sug_梗图素材双人_r2_q3_2",
+      "sug_梗图素材单人_r2_q3_3",
+      "sug_梗图素材原图_r2_q3_4",
+      "sug_梗图素材多人_r2_q3_5",
+      "sug_梗图素材画画_r2_q3_6",
+      "sug_梗图素材描改_r2_q3_7",
+      "sug_梗图素材表情包_r2_q3_8",
+      "sug_梗图素材聊天记录_r2_q3_9",
+      "sug_梗图模版抽象_r2_q4_0",
+      "sug_梗图模版双人_r2_q4_1",
+      "sug_梗图模版单人_r2_q4_2",
+      "sug_梗图模板_r2_q4_3",
+      "sug_梗图模版三人_r2_q4_4",
+      "sug_梗图模版表情包_r2_q4_5",
+      "sug_梗图模版多人_r2_q4_6",
+      "sug_梗图模版四人_r2_q4_7",
+      "sug_双眼皮模版图_r2_q4_8",
+      "sug_梗图模版表格_r2_q4_9",
+      "sug_梗图描改接稿_r2_q5_0",
+      "sug_梗图描改素材_r2_q5_1",
+      "sug_梗图描改教程_r2_q5_2",
+      "sug_梗图描改怎么画_r2_q5_3",
+      "sug_梗图描改模板_r2_q5_4",
+      "sug_梗图描改约稿_r2_q5_5",
+      "sug_梗图描改原图_r2_q5_6",
+      "sug_梗图描改鸡蛋_r2_q5_7",
+      "sug_抽象梗图描改_r2_q5_8",
+      "sug_梗图描改过程_r2_q5_9",
+      "sug_表情包简笔画可爱_r2_q6_0",
+      "sug_表情包简笔画抽象_r2_q6_1",
+      "sug_表情包简笔画大全_r2_q6_2",
+      "sug_表情包简笔画哭_r2_q6_3",
+      "sug_表情包简笔画图片_r2_q6_4",
+      "sug_表情包简笔画人物_r2_q6_5",
+      "sug_表情包简笔画教程_r2_q6_6",
+      "sug_表情简笔画_r2_q6_7",
+      "sug_表情包简笔画加油_r2_q6_8",
+      "sug_表情包简笔画开心_r2_q6_9",
+      "sug_讽刺双标的图片_r2_q7_0",
+      "sug_暗示双标的图片_r2_q7_1",
+      "sug_双标搞笑图片_r2_q7_2",
+      "sug_讽刺双标的人的文案_r2_q7_3",
+      "sug_双标梗_r2_q7_4",
+      "sug_双标是什么意思_r2_q7_5",
+      "sug_形容双标人的文案_r2_q7_6",
+      "sug_内涵双标的文案_r2_q7_7",
+      "sug_卡皮巴拉100种可爱图片_r2_q7_8",
+      "sug_双标_r2_q7_9",
+      "sug_表情包图片大全简笔画_r2_q8_0",
+      "sug_表情包图片大全微信_r2_q8_1",
+      "sug_表情包图片大全抽象_r2_q8_2",
+      "sug_表情包图片大全可爱_r2_q8_3",
+      "sug_表情包图片大全动态_r2_q8_4",
+      "sug_表情包图片大全素材_r2_q8_5",
+      "sug_表情包图片大全搞笑_r2_q8_6",
+      "sug_ch表情包图片大全_r2_q8_7",
+      "sug_开心表情包图片大全_r2_q8_8",
+      "sug_表情包图片大全打印素材_r2_q8_9",
+      "sug_梗图meme双人_r2_q9_0",
+      "sug_梗图meme模板_r2_q9_1",
+      "sug_心理疾病meme梗图_r2_q9_2",
+      "sug_梗图meme素材_r2_q9_3",
+      "sug_梗图meme悲伤自嘲_r2_q9_4",
+      "sug_梗图meme代餐_r2_q9_5",
+      "sug_梗图meme学习_r2_q9_6",
+      "sug_焦虑症meme梗图_r2_q9_7",
+      "sug_精神疾病meme梗图_r2_q9_8",
+      "sug_梗图meme原创_r2_q9_9",
+      "sug_恋与深空同人图资源分享_r2_q10_0",
+      "sug_早安分享图片_r2_q10_1",
+      "sug_定制分享图_r2_q10_2",
+      "sug_图纸分享_r2_q10_3",
+      "sug_meme梗图分享_r2_q10_4",
+      "sug_拼豆豆图纸分享_r2_q10_5",
+      "sug_买断图分享_r2_q10_6",
+      "sug_拼豆图纸分享_r2_q10_7",
+      "sug_日常分享图片_r2_q10_8",
+      "sug_自截分享图_r2_q10_9",
+      "sug_猫咪表情包_r2_q11_0",
+      "sug_猫咪表情包图片_r2_q11_1",
+      "sug_猫咪咪表情包图片_r2_q11_2",
+      "sug_猫咪搞笑表情包_r2_q11_3",
+      "sug_猫咪表情包梗图搞笑_r2_q11_4",
+      "sug_猫咪表情包抽象_r2_q12_0",
+      "sug_猫meme表情包动图_r2_q12_1",
+      "sug_猫咪表情包配文_r2_q12_2",
+      "sug_猫咪表情包动图_r2_q12_3",
+      "sug_猫咪表情包制作_r2_q12_4",
+      "sug_猫咪表情包可爱_r2_q12_5",
+      "sug_猫咪表情符号_r2_q12_6",
+      "sug_猫咪表情包叫什么_r2_q12_7",
+      "sug_猫咪表情包图片_r2_q12_8",
+      "sug_猫咪表情包视频_r2_q12_9",
+      "sug_猫咪梗图素材_r2_q13_0",
+      "sug_猫咪梗图模板_r2_q13_1",
+      "sug_猫咪梗图骂人_r2_q13_2",
+      "sug_猫咪梗图恋爱_r2_q13_3",
+      "sug_猫咪梗图伤感_r2_q13_4",
+      "sug_猫咪梗图睡觉_r2_q13_5",
+      "sug_猫咪梗图英文_r2_q13_6",
+      "sug_猫咪梗图塔牌_r2_q13_7",
+      "sug_猫咪梗图拍照_r2_q13_8",
+      "sug_猫咪梗图上帝_r2_q13_9",
+      "sug_表情包梗图模板_r2_q14_0",
+      "sug_表情包梗图抽象_r2_q14_1",
+      "sug_jojo表情包梗图_r2_q14_2",
+      "sug_表情包梗图学习_r2_q14_3",
+      "sug_表情包梗图动图_r2_q14_4",
+      "sug_表情包梗图cp_r2_q14_5",
+      "sug_重返未来表情包梗图_r2_q14_6",
+      "sug_表情包梗图可爱_r2_q14_7",
+      "sug_表情包梗图恶俗_r2_q14_8",
+      "sug_表情包梗图开心_r2_q14_9",
+      "sug_反映人类双胞胎基因变异_r2_q15_0",
+      "sug_反映人类双标行为的图片_r2_q16_0",
+      "sug_人类双标行为是什么意思_r2_q17_0",
+      "sug_双标行为举例_r2_q18_0",
+      "sug_双标行为内涵是什么_r2_q18_1",
+      "sug_讽刺双标的文案_r2_q18_2",
+      "sug_王楚钦双标行为_r2_q18_3",
+      "sug_猫咪为什么会出现双标行为_r2_q18_4",
+      "sug_双标男友的典型表现_r2_q18_5",
+      "sug_猫咪双标行为搞笑视频_r2_q18_6",
+      "sug_猫咪双标行为_r2_q18_7",
+      "sug_双标是什么意思啊_r2_q18_8",
+      "sug_双标的人怎么怼_r2_q18_9",
+      "sug_反映人类社会发展进步的价值理念_r2_q19_0",
+      "sug_人类图反映者_r2_q19_1",
+      "sug_怎么识别人类图中的反映者_r2_q19_2",
+      "sug_人类图显示者_r2_q19_3",
+      "sug_人类图解读_r2_q19_4",
+      "sug_人类图是什么_r2_q19_5",
+      "sug_反映者适合什么工作_r2_q19_6",
+      "sug_人类图投射者_r2_q19_7",
+      "sug_反映者的代表人物_r2_q19_8",
+      "sug_人类图测试_r2_q19_9",
+      "sug_反映人类行为的环境问题_r2_q20_0",
+      "sug_人类行为与社会环境_r2_q21_0",
+      "sug_人类行为设计师-小周_r2_q21_1",
+      "sug_人类行为与社会环境重点_r2_q21_2",
+      "sug_人类行为与社会环境笔记_r2_q21_3",
+      "sug_人类行为矫正教育漫画_r2_q21_4",
+      "sug_人类行为与社会环境第三版_r2_q21_5",
+      "sug_人类行为矫正教育_r2_q21_6",
+      "sug_人类行为心理学_r2_q21_7",
+      "sug_人类行为观察员_r2_q21_8",
+      "sug_人类行为艺术_r2_q21_9",
+      "post_66f9046a000000002a033202_0_0",
+      "post_668e06cd00000000250145fe_0_1",
+      "post_67dc1a58000000001d0232da_0_2",
+      "post_68282bbd000000002001f349_0_3",
+      "post_66ea4e30000000001e01964b_0_4",
+      "post_6690ebe500000000250153e5_0_5",
+      "post_671d9a72000000001402e50f_0_6",
+      "post_668f5479000000000d00ebc2_0_7",
+      "post_680054bc000000001d014b19_0_8",
+      "post_690818e70000000003011fa5_0_9",
+      "post_66af92ad0000000005038228_1_0",
+      "post_68597fed000000001202d443_1_1",
+      "post_6541e7fb000000002303bfea_1_2",
+      "post_67bb4d7d000000002903d2fb_1_3",
+      "post_6791c3e600000000170392b9_1_4",
+      "post_69080c43000000000402bb4b_1_5",
+      "post_66791a5f000000001c027fab_1_6",
+      "post_66e0e9b900000000270026f2_1_7",
+      "post_67db7618000000001c008ddb_1_8",
+      "post_66caccf2000000001f03bcee_1_9",
+      "post_690ec7070000000004022819_2_0",
+      "post_66877a00000000000a0073d7_2_1",
+      "post_68ae7e48000000001c004610_2_2",
+      "post_6697b8310000000025003164_2_3",
+      "post_68626478000000000d019251_2_4",
+      "post_68dde20600000000070173ef_2_5",
+      "post_67dd3945000000001d02d900_2_6",
+      "post_6907666b0000000004022dbb_2_7",
+      "post_68a43430000000001d0027d6_2_8",
+      "post_67d2e30d000000000603eb03_2_9",
+      "post_68ef8753000000000301d581_3_0",
+      "post_6882582c000000000d018245_3_1",
+      "post_689937da0000000022023e1e_3_2",
+      "post_68fdde910000000003018b60_3_3",
+      "post_63cfabdd000000001b0173d5_3_4",
+      "post_62e4f79a000000000e0308ed_3_5",
+      "post_68429814000000002001dfc8_3_6",
+      "post_68f485a60000000003010182_3_7",
+      "post_682b5cc3000000000f031bf9_3_8",
+      "post_681c78d50000000020028a5c_3_9",
+      "post_68e06587000000000401467d_4_0",
+      "post_690a02720000000005002039_4_1",
+      "post_68b27225000000001b03e3a4_4_2",
+      "post_677e6b540000000001000781_4_3",
+      "post_6640beca000000001e023a10_4_4",
+      "post_67a38120000000001801354d_4_5",
+      "post_66f6d0df000000002c029dc7_4_6",
+      "post_686cfdf60000000023005633_4_7",
+      "post_674db30c00000000070278f1_4_8",
+      "post_6913214e0000000003034f7f_4_9",
+      "post_67d2310a000000001d026c02_5_0",
+      "post_68d8bddf000000000e031155_5_1",
+      "post_6710c5a7000000001402dc9f_5_2",
+      "post_6731806d000000001b02cf7f_5_3",
+      "post_66505e0c000000001401add3_5_4",
+      "post_6629f62a00000000030233d9_5_5",
+      "post_68e71e340000000007002e56_5_6",
+      "post_67cbe638000000001203e839_5_7",
+      "post_6826f7b7000000002202a171_5_8",
+      "post_666d59f2000000000e032b89_5_9",
+      "post_660581800000000012023abc_6_0",
+      "post_678c8258000000001b00b500_6_1",
+      "post_67f60c23000000001c03e8b0_6_2",
+      "post_6618ef8a000000001b00815c_6_3",
+      "post_68650c0e000000002400ec34_6_4",
+      "post_67541e26000000000800553c_6_5",
+      "post_68ee4f9400000000050318ba_6_6",
+      "post_6803ae1a000000001c036da7_6_7",
+      "post_66209a4c00000000040180ad_6_8",
+      "post_618ff44f0000000001029e17_6_9"
+    ]
+  },
+  "fullData": null
+};
+
+// 根据节点类型获取边框颜色
+function getNodeTypeColor(type) {
+  const typeColors = {
+    'root': '#6b21a8',        // 紫色 - 根节点
+    'round': '#7c3aed',       // 深紫 - Round节点
+    'step': '#f59e0b',        // 橙色 - 步骤节点
+    'seg': '#10b981',         // 绿色 - 分词
+    'q': '#3b82f6',           // 蓝色 - Query
+    'sug': '#06b6d4',         // 青色 - Sug建议词
+    'seed': '#84cc16',        // 黄绿 - Seed
+    'add_word': '#22c55e',    // 绿色 - 加词生成
+    'search_word': '#8b5cf6', // 紫色 - 搜索词
+    'post': '#ec4899',        // 粉色 - 帖子
+    'filtered_sug': '#14b8a6',// 青绿 - 筛选的sug
+    'next_q': '#2563eb',      // 深蓝 - 下轮查询
+    'next_seed': '#65a30d',   // 深黄绿 - 下轮种子
+    'search': '#8b5cf6',      // 深紫 - 搜索(兼容旧版)
+    'operation': '#f59e0b',   // 橙色 - 操作节点(兼容旧版)
+    'query': '#3b82f6',       // 蓝色 - 查询(兼容旧版)
+    'note': '#ec4899',        // 粉色 - 帖子(兼容旧版)
+  };
+  return typeColors[type] || '#9ca3af';
+}
+
+// 查询节点组件 - 卡片样式
+function QueryNode({ id, data, sourcePosition, targetPosition }) {
+  // 所有节点默认展开
+  const expanded = true;
+
+  // 获取节点类型颜色
+  const typeColor = getNodeTypeColor(data.nodeType || 'query');
+
+  return (
+    <div>
+      <Handle
+        type="target"
+        position={targetPosition || Position.Left}
+        style={{ background: typeColor, width: 8, height: 8 }}
+      />
+      <div
+        style={{
+          padding: '12px',
+          borderRadius: '8px',
+          border: data.isHighlighted ? `3px solid ${typeColor}` :
+                  data.isCollapsed ? `2px solid ${typeColor}` :
+                  data.isSelected === false ? '2px dashed #d1d5db' :
+                  `2px solid ${typeColor}`,
+          background: data.isHighlighted ? '#eef2ff' :
+                      data.isSelected === false ? '#f9fafb' : 'white',
+          minWidth: '200px',
+          maxWidth: '280px',
+          boxShadow: data.isHighlighted ? '0 0 0 4px rgba(102, 126, 234, 0.25), 0 4px 16px rgba(102, 126, 234, 0.4)' :
+                     data.isCollapsed ? '0 4px 12px rgba(102, 126, 234, 0.15)' :
+                     data.level === 0 ? '0 4px 12px rgba(139, 92, 246, 0.15)' : '0 2px 6px rgba(0, 0, 0, 0.06)',
+          transition: 'all 0.3s ease',
+          cursor: 'pointer',
+          position: 'relative',
+          opacity: data.isSelected === false ? 0.6 : 1,
+        }}
+      >
+        {/* 折叠当前节点按钮 - 左边 */}
+        <div
+          style={{
+            position: 'absolute',
+            top: '6px',
+            left: '6px',
+            width: '20px',
+            height: '20px',
+            borderRadius: '50%',
+            background: '#f59e0b',
+            color: 'white',
+            display: 'flex',
+            alignItems: 'center',
+            justifyContent: 'center',
+            fontSize: '11px',
+            fontWeight: 'bold',
+            cursor: 'pointer',
+            transition: 'all 0.2s ease',
+            zIndex: 10,
+          }}
+          onClick={(e) => {
+            e.stopPropagation();
+            if (data.onHideSelf) {
+              data.onHideSelf();
+            }
+          }}
+          onMouseEnter={(e) => {
+            e.currentTarget.style.background = '#d97706';
+          }}
+          onMouseLeave={(e) => {
+            e.currentTarget.style.background = '#f59e0b';
+          }}
+          title="隐藏当前节点"
+        >
+          ×
+        </div>
+
+        {/* 聚焦按钮 - 右上角 */}
+        <div
+          style={{
+            position: 'absolute',
+            top: '6px',
+            right: '6px',
+            width: '20px',
+            height: '20px',
+            borderRadius: '50%',
+            background: data.isFocused ? '#10b981' : '#e5e7eb',
+            color: data.isFocused ? 'white' : '#6b7280',
+            display: 'flex',
+            alignItems: 'center',
+            justifyContent: 'center',
+            fontSize: '11px',
+            fontWeight: 'bold',
+            cursor: 'pointer',
+            transition: 'all 0.2s ease',
+            zIndex: 10,
+          }}
+          onClick={(e) => {
+            e.stopPropagation();
+            if (data.onFocus) {
+              data.onFocus();
+            }
+          }}
+          onMouseEnter={(e) => {
+            if (!data.isFocused) {
+              e.currentTarget.style.background = '#d1d5db';
+            }
+          }}
+          onMouseLeave={(e) => {
+            if (!data.isFocused) {
+              e.currentTarget.style.background = '#e5e7eb';
+            }
+          }}
+          title={data.isFocused ? '取消聚焦' : '聚焦到此节点'}
+        >
+          🎯
+        </div>
+
+        {/* 折叠/展开子节点按钮 - 右边第二个位置 */}
+        {data.hasChildren && (
+          <div
+            style={{
+              position: 'absolute',
+              top: '6px',
+              right: '30px',
+              width: '20px',
+              height: '20px',
+              borderRadius: '50%',
+              background: data.isCollapsed ? '#667eea' : '#e5e7eb',
+              color: data.isCollapsed ? 'white' : '#6b7280',
+              display: 'flex',
+              alignItems: 'center',
+              justifyContent: 'center',
+              fontSize: '11px',
+              fontWeight: 'bold',
+              cursor: 'pointer',
+              transition: 'all 0.2s ease',
+              zIndex: 10,
+            }}
+            onClick={(e) => {
+              e.stopPropagation();
+              data.onToggleCollapse();
+            }}
+            title={data.isCollapsed ? '展开子节点' : '折叠子节点'}
+          >
+            {data.isCollapsed ? '+' : '−'}
+          </div>
+        )}
+
+        {/* 卡片内容 */}
+        <div>
+          {/* 标题行 */}
+          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: '8px', paddingLeft: '24px', paddingRight: data.hasChildren ? '54px' : '28px' }}>
+            <div style={{ flex: 1 }}>
+              <div style={{ display: 'flex', alignItems: 'center', gap: '4px', marginBottom: '3px' }}>
+                <div style={{
+                  fontSize: '13px',
+                  fontWeight: data.level === 0 ? '700' : '600',
+                  color: data.level === 0 ? '#6b21a8' : '#1f2937',
+                  lineHeight: '1.3',
+                  flex: 1,
+                }}>
+                  {data.title}
+                </div>
+                {data.isSelected === false && (
+                  <div style={{
+                    fontSize: '9px',
+                    padding: '1px 4px',
+                    borderRadius: '3px',
+                    background: '#fee2e2',
+                    color: '#991b1b',
+                    fontWeight: '500',
+                    flexShrink: 0,
+                  }}>
+                    未选中
+                  </div>
+                )}
+              </div>
+            </div>
+          </div>
+
+        {/* 展开的详细信息 - 始终显示 */}
+        <div style={{ fontSize: '11px', lineHeight: 1.4 }}>
+            <div style={{ display: 'flex', gap: '4px', marginBottom: '6px', flexWrap: 'wrap' }}>
+              <span style={{
+                display: 'inline-block',
+                padding: '1px 6px',
+                borderRadius: '10px',
+                background: '#eff6ff',
+                color: '#3b82f6',
+                fontSize: '10px',
+                fontWeight: '500',
+              }}>
+                Lv.{data.level}
+              </span>
+              <span style={{
+                display: 'inline-block',
+                padding: '1px 6px',
+                borderRadius: '10px',
+                background: '#f0fdf4',
+                color: '#16a34a',
+                fontSize: '10px',
+                fontWeight: '500',
+              }}>
+                {data.score}
+              </span>
+              {data.strategy && data.strategy !== 'root' && (
+                <span style={{
+                  display: 'inline-block',
+                  padding: '1px 6px',
+                  borderRadius: '10px',
+                  background: '#fef3c7',
+                  color: '#92400e',
+                  fontSize: '10px',
+                  fontWeight: '500',
+                }}>
+                  {data.strategy}
+                </span>
+              )}
+              {(data.typeLabel || data.type_label) && (
+                <span style={{
+                  display: 'inline-block',
+                  padding: '1px 6px',
+                  borderRadius: '10px',
+                  background: '#fce7f3',
+                  color: '#9f1239',
+                  fontSize: '10px',
+                  fontWeight: '500',
+                }}>
+                  {data.typeLabel || data.type_label}
+                </span>
+              )}
+            {data.is_suggestion && data.suggestion_label && (
+              <span style={{
+                display: 'inline-block',
+                padding: '1px 6px',
+                borderRadius: '10px',
+                background: '#ede9fe',
+                color: '#6d28d9',
+                fontSize: '10px',
+                fontWeight: '600',
+              }}>
+                {data.suggestion_label}
+              </span>
+            )}
+            </div>
+
+            {data.parent && (
+              <div style={{ color: '#6b7280', fontSize: '10px', marginTop: '4px', paddingTop: '4px', borderTop: '1px solid #f3f4f6' }}>
+                <strong>Parent:</strong> {data.parent}
+              </div>
+            )}
+            {data.nodeType === 'domain_combination' && Array.isArray(data.source_word_details) && data.source_word_details.length > 0 && (
+              <div style={{
+                marginTop: '6px',
+                paddingTop: '6px',
+                borderTop: '1px solid #f3f4f6',
+                fontSize: '10px',
+                color: '#6b7280',
+                lineHeight: '1.5',
+              }}>
+                <strong style={{ color: '#4b5563' }}>来源词得分:</strong>
+                <div style={{ marginTop: '4px', display: 'flex', flexDirection: 'column', gap: '4px' }}>
+                  {data.source_word_details.map((detail, idx) => {
+                    const words = (detail.words || []).map((w) => {
+                      const numericScore = typeof w.score === 'number' ? w.score : parseFloat(w.score || '0');
+                      const formattedScore = Number.isFinite(numericScore) ? numericScore.toFixed(2) : '0.00';
+                      return w.text + ' (' + formattedScore + ')';
+                    }).join(' + ');
+                    return (
+                      <div key={idx} style={{ display: 'flex', flexWrap: 'wrap', gap: '4px', alignItems: 'center' }}>
+                        <span style={{ color: '#2563eb' }}>{words}</span>
+                      </div>
+                    );
+                  })}
+                </div>
+                <div style={{ marginTop: '4px', fontWeight: '500', color: data.is_above_sources ? '#16a34a' : '#dc2626' }}>
+                  {data.is_above_sources ? '✅ 组合得分高于所有来源词' : '⚠️ 组合得分未超过全部来源词'}
+                </div>
+              </div>
+            )}
+            {data.selectedWord && (
+              <div style={{
+                marginTop: '6px',
+                paddingTop: '6px',
+                borderTop: '1px solid #f3f4f6',
+                fontSize: '10px',
+                color: '#6b7280',
+                lineHeight: '1.5',
+              }}>
+                <strong style={{ color: '#4b5563' }}>选择词:</strong>
+                <span style={{ marginLeft: '4px', color: '#3b82f6', fontWeight: '500' }}>{data.selectedWord}</span>
+                {data.seed_score !== undefined && (
+                  <div style={{ marginTop: '4px' }}>
+                    <strong style={{ color: '#4b5563' }}>种子得分:</strong>
+                    <span style={{ marginLeft: '4px', color: '#16a34a', fontWeight: '500' }}>
+                      {typeof data.seed_score === 'number' ? data.seed_score.toFixed(2) : data.seed_score}
+                    </span>
+                  </div>
+                )}
+              </div>
+            )}
+            {data.evaluationReason && (
+              <div style={{
+                marginTop: '6px',
+                paddingTop: '6px',
+                borderTop: '1px solid #f3f4f6',
+                fontSize: '10px',
+                color: '#6b7280',
+                lineHeight: '1.5',
+              }}>
+                <strong style={{ color: '#4b5563' }}>评估:</strong>
+                <div style={{ marginTop: '2px' }}>{data.evaluationReason}</div>
+              </div>
+            )}
+            {data.occurrences && data.occurrences.length > 1 && (
+              <div style={{
+                marginTop: '6px',
+                paddingTop: '6px',
+                borderTop: '1px solid #f3f4f6',
+                fontSize: '10px',
+                color: '#6b7280',
+              }}>
+                <strong style={{ color: '#4b5563' }}>演化历史 ({data.occurrences.length}次):</strong>
+                <div style={{ marginTop: '4px' }}>
+                  {data.occurrences.map((occ, idx) => (
+                    <div key={idx} style={{ marginTop: '2px', paddingLeft: '8px' }}>
+                      <span style={{ color: '#3b82f6', fontWeight: '500' }}>R{occ.round}</span>
+                      {' · '}
+                      <span>{occ.strategy}</span>
+                      {occ.score !== undefined && (
+                        <span style={{ color: '#16a34a', marginLeft: '4px' }}>
+                          ({typeof occ.score === 'number' ? occ.score.toFixed(2) : occ.score})
+                        </span>
+                      )}
+                    </div>
+                  ))}
+                </div>
+              </div>
+            )}
+            {data.hasSearchResults && (
+              <div style={{
+                marginTop: '6px',
+                paddingTop: '6px',
+                borderTop: '1px solid #f3f4f6',
+                fontSize: '10px',
+                background: '#fef3c7',
+                padding: '4px 6px',
+                borderRadius: '4px',
+                color: '#92400e',
+                fontWeight: '500',
+              }}>
+                🔍 找到 {data.postCount} 个帖子
+              </div>
+            )}
+          </div>
+        </div>
+      </div>
+      <Handle
+        type="source"
+        position={sourcePosition || Position.Right}
+        style={{ background: '#667eea', width: 8, height: 8 }}
+      />
+    </div>
+  );
+}
+
+// 笔记节点组件 - 卡片样式,带轮播图
+function NoteNode({ id, data, sourcePosition, targetPosition }) {
+  const [currentImageIndex, setCurrentImageIndex] = useState(0);
+  const expanded = true;
+  const hasImages = data.imageList && data.imageList.length > 0;
+
+  const nextImage = (e) => {
+    e.stopPropagation();
+    if (hasImages) {
+      setCurrentImageIndex((prev) => (prev + 1) % data.imageList.length);
+    }
+  };
+
+  const prevImage = (e) => {
+    e.stopPropagation();
+    if (hasImages) {
+      setCurrentImageIndex((prev) => (prev - 1 + data.imageList.length) % data.imageList.length);
+    }
+  };
+
+  return (
+    <div>
+      <Handle
+        type="target"
+        position={targetPosition || Position.Left}
+        style={{ background: '#ec4899', width: 8, height: 8 }}
+      />
+      <div
+        style={{
+          padding: '14px',
+          borderRadius: '20px',
+          border: data.isHighlighted ? '3px solid #ec4899' : '2px solid #fce7f3',
+          background: data.isHighlighted ? '#eef2ff' : 'white',
+          minWidth: '220px',
+          maxWidth: '300px',
+          boxShadow: data.isHighlighted ? '0 0 0 4px rgba(236, 72, 153, 0.25), 0 4px 16px rgba(236, 72, 153, 0.4)' : '0 4px 12px rgba(236, 72, 153, 0.15)',
+          transition: 'all 0.3s ease',
+          cursor: 'pointer',
+        }}
+      >
+        {/* 笔记标题 */}
+        <div style={{ display: 'flex', alignItems: 'flex-start', marginBottom: '8px' }}>
+          <div style={{ flex: 1 }}>
+            <div style={{
+              fontSize: '13px',
+              fontWeight: '600',
+              color: '#831843',
+              lineHeight: '1.4',
+              marginBottom: '4px',
+            }}>
+              {data.title}
+            </div>
+          </div>
+        </div>
+
+        {/* 评估信息区域 */}
+        {(data.is_knowledge !== undefined && data.is_knowledge !== null || data.post_relevance_score !== undefined && data.post_relevance_score !== null) && (
+          <div style={{
+            marginBottom: '10px',
+            paddingBottom: '8px',
+            borderBottom: '1px solid #fce7f3',
+          }}>
+            {/* 知识判定标签 */}
+            {(data.is_knowledge !== undefined && data.is_knowledge !== null) && (
+              <div style={{ marginBottom: '8px' }}>
+                <span style={{
+                  display: 'inline-block',
+                  padding: '3px 10px',
+                  borderRadius: '12px',
+                  fontSize: '11px',
+                  fontWeight: '600',
+                  background: data.is_knowledge ? '#dcfce7' : '#fee2e2',
+                  color: data.is_knowledge ? '#166534' : '#991b1b',
+                }}>
+                  {data.is_knowledge ? '✓ 知识内容' : '✗ 非知识'}
+                </span>
+                {data.knowledge_reason && (
+                  <div style={{
+                    marginTop: '4px',
+                    fontSize: '10px',
+                    color: '#9f1239',
+                    lineHeight: '1.4',
+                  }}>
+                    {data.knowledge_reason}
+                  </div>
+                )}
+              </div>
+            )}
+
+            {/* 相关性得分 */}
+            {(data.post_relevance_score !== undefined && data.post_relevance_score !== null) && (
+              <div>
+                <div style={{
+                  display: 'flex',
+                  alignItems: 'center',
+                  gap: '6px',
+                  marginBottom: '4px',
+                }}>
+                  <span style={{
+                    fontSize: '11px',
+                    fontWeight: '600',
+                    color: '#9f1239',
+                  }}>
+                    相关性: {(data.post_relevance_score * 100).toFixed(0)}%
+                  </span>
+                  {data.relevance_level && (
+                    <span style={{
+                      padding: '2px 8px',
+                      borderRadius: '10px',
+                      fontSize: '10px',
+                      fontWeight: '600',
+                      background:
+                        data.relevance_level === '高度相关' ? '#dcfce7' :
+                        data.relevance_level === '中度相关' ? '#fef3c7' : '#fee2e2',
+                      color:
+                        data.relevance_level === '高度相关' ? '#166534' :
+                        data.relevance_level === '中度相关' ? '#854d0e' : '#991b1b',
+                    }}>
+                      {data.relevance_level}
+                    </span>
+                  )}
+                </div>
+                {data.relevance_reason && (
+                  <div style={{
+                    fontSize: '10px',
+                    color: '#9f1239',
+                    lineHeight: '1.4',
+                  }}>
+                    {data.relevance_reason}
+                  </div>
+                )}
+              </div>
+            )}
+          </div>
+        )}
+
+        {/* 轮播图 */}
+        {hasImages && (
+          <div style={{
+            position: 'relative',
+            marginBottom: '8px',
+            borderRadius: '12px',
+            overflow: 'hidden',
+          }}>
+            <img
+              src={data.imageList[currentImageIndex].image_url}
+              alt={`Image ${currentImageIndex + 1}`}
+              style={{
+                width: '100%',
+                height: '160px',
+                objectFit: 'cover',
+                display: 'block',
+              }}
+              onError={(e) => {
+                e.target.style.display = 'none';
+              }}
+            />
+            {data.imageList.length > 1 && (
+              <>
+                {/* 左右切换按钮 */}
+                <button
+                  onClick={prevImage}
+                  style={{
+                    position: 'absolute',
+                    left: '4px',
+                    top: '50%',
+                    transform: 'translateY(-50%)',
+                    background: 'rgba(0, 0, 0, 0.5)',
+                    color: 'white',
+                    border: 'none',
+                    borderRadius: '50%',
+                    width: '24px',
+                    height: '24px',
+                    cursor: 'pointer',
+                    display: 'flex',
+                    alignItems: 'center',
+                    justifyContent: 'center',
+                    fontSize: '14px',
+                  }}
+                >
+                  ‹
+                </button>
+                <button
+                  onClick={nextImage}
+                  style={{
+                    position: 'absolute',
+                    right: '4px',
+                    top: '50%',
+                    transform: 'translateY(-50%)',
+                    background: 'rgba(0, 0, 0, 0.5)',
+                    color: 'white',
+                    border: 'none',
+                    borderRadius: '50%',
+                    width: '24px',
+                    height: '24px',
+                    cursor: 'pointer',
+                    display: 'flex',
+                    alignItems: 'center',
+                    justifyContent: 'center',
+                    fontSize: '14px',
+                  }}
+                >
+                  ›
+                </button>
+                {/* 图片计数 */}
+                <div style={{
+                  position: 'absolute',
+                  bottom: '4px',
+                  right: '4px',
+                  background: 'rgba(0, 0, 0, 0.6)',
+                  color: 'white',
+                  padding: '2px 6px',
+                  borderRadius: '10px',
+                  fontSize: '10px',
+                }}>
+                  {currentImageIndex + 1}/{data.imageList.length}
+                </div>
+              </>
+            )}
+          </div>
+        )}
+
+        {/* 互动数据 */}
+        {data.interact_info && (
+          <div style={{
+            display: 'flex',
+            gap: '8px',
+            marginBottom: '8px',
+            flexWrap: 'wrap',
+            fontSize: '11px',
+            color: '#9f1239',
+          }}>
+            {data.interact_info.liked_count > 0 && (
+              <span style={{ display: 'flex', alignItems: 'center', gap: '2px' }}>
+                ❤️ {data.interact_info.liked_count}
+              </span>
+            )}
+            {data.interact_info.collected_count > 0 && (
+              <span style={{ display: 'flex', alignItems: 'center', gap: '2px' }}>
+                ⭐ {data.interact_info.collected_count}
+              </span>
+            )}
+            {data.interact_info.comment_count > 0 && (
+              <span style={{ display: 'flex', alignItems: 'center', gap: '2px' }}>
+                💬 {data.interact_info.comment_count}
+              </span>
+            )}
+            {data.interact_info.shared_count > 0 && (
+              <span style={{ display: 'flex', alignItems: 'center', gap: '2px' }}>
+                🔗 {data.interact_info.shared_count}
+              </span>
+            )}
+          </div>
+        )}
+
+        {/* 被哪些query找到 */}
+        {data.foundByQueries && data.foundByQueries.length > 0 && (
+          <div style={{
+            marginBottom: '8px',
+            padding: '6px 8px',
+            background: '#f0fdf4',
+            borderRadius: '6px',
+            fontSize: '10px',
+          }}>
+            <strong style={{ color: '#16a34a' }}>🔍 被找到:</strong>
+            <div style={{ marginTop: '4px', display: 'flex', flexWrap: 'wrap', gap: '4px' }}>
+              {data.foundByQueries.map((query, idx) => (
+                <span key={idx} style={{
+                  display: 'inline-block',
+                  padding: '2px 6px',
+                  background: '#dcfce7',
+                  color: '#166534',
+                  borderRadius: '4px',
+                  fontSize: '9px',
+                }}>
+                  {query}
+                </span>
+              ))}
+            </div>
+            {data.foundInRounds && data.foundInRounds.length > 0 && (
+              <div style={{ marginTop: '4px', color: '#6b7280' }}>
+                出现在: Round {data.foundInRounds.join(', ')}
+              </div>
+            )}
+          </div>
+        )}
+
+        {/* 标签 */}
+        {(data.matchLevel || data.score) && (
+          <div style={{ display: 'flex', gap: '6px', marginBottom: '8px', flexWrap: 'wrap' }}>
+            {data.matchLevel && (
+              <span style={{
+                display: 'inline-block',
+                padding: '2px 8px',
+                borderRadius: '12px',
+                background: '#fff1f2',
+                color: '#be123c',
+                fontSize: '10px',
+                fontWeight: '500',
+              }}>
+                {data.matchLevel}
+              </span>
+            )}
+            {data.score && (
+              <span style={{
+                display: 'inline-block',
+                padding: '2px 8px',
+                borderRadius: '12px',
+                background: '#fff7ed',
+                color: '#c2410c',
+                fontSize: '10px',
+                fontWeight: '500',
+              }}>
+                Score: {data.score}
+              </span>
+            )}
+          </div>
+        )}
+
+        {/* 描述 */}
+        {expanded && data.description && (
+          <div style={{
+            fontSize: '11px',
+            color: '#9f1239',
+            lineHeight: '1.5',
+            paddingTop: '8px',
+            borderTop: '1px solid #fbcfe8',
+          }}>
+            {data.description}
+          </div>
+        )}
+
+        {/* 评估理由 */}
+        {expanded && data.evaluationReason && (
+          <div style={{
+            fontSize: '10px',
+            color: '#831843',
+            lineHeight: '1.5',
+            paddingTop: '8px',
+            marginTop: '8px',
+            borderTop: '1px solid #fbcfe8',
+          }}>
+            <strong style={{ color: '#9f1239' }}>评估:</strong>
+            <div style={{ marginTop: '2px' }}>{data.evaluationReason}</div>
+          </div>
+        )}
+      </div>
+      <Handle
+        type="source"
+        position={sourcePosition || Position.Right}
+        style={{ background: '#ec4899', width: 8, height: 8 }}
+      />
+    </div>
+  );
+}
+
+// AnalysisNode 组件:展示AI分析(左侧OCR文字,右侧缩略图+描述)
+function AnalysisNode({ data }) {
+  const nodeStyle = {
+    background: '#fffbeb',
+    border: '2px solid #fbbf24',
+    borderRadius: '8px',
+    padding: '12px',
+    minWidth: '700px',
+    maxWidth: '900px',
+    fontSize: '12px',
+    boxShadow: '0 4px 6px rgba(0,0,0,0.1)',
+  };
+
+  return (
+    <div style={nodeStyle}>
+      <Handle
+        type="target"
+        position={Position.Left}
+        style={{ background: '#fbbf24', width: 8, height: 8 }}
+      />
+
+      {/* 标题 */}
+      <div style={{
+        fontSize: '14px',
+        fontWeight: 'bold',
+        marginBottom: '8px',
+        color: '#92400e',
+      }}>
+        🖼️ {data.query}
+      </div>
+
+      {/* 评分和互动数据 */}
+      <div style={{
+        display: 'flex',
+        justifyContent: 'space-between',
+        marginBottom: '8px',
+        padding: '6px',
+        background: '#fef3c7',
+        borderRadius: '4px',
+      }}>
+        <div style={{ fontSize: '11px', fontWeight: 'bold' }}>
+          Score: {data.interact_info?.relevance_score || 0}
+        </div>
+        <div style={{ display: 'flex', gap: '12px', fontSize: '11px' }}>
+          {data.interact_info?.liked_count > 0 && (
+            <span>❤️ {data.interact_info.liked_count}</span>
+          )}
+          {data.interact_info?.collected_count > 0 && (
+            <span>⭐ {data.interact_info.collected_count}</span>
+          )}
+          {data.interact_info?.comment_count > 0 && (
+            <span>💬 {data.interact_info.comment_count}</span>
+          )}
+        </div>
+      </div>
+
+      {/* 完整正文内容 */}
+      {data.body_text && (
+        <div style={{
+          padding: '8px',
+          background: 'white',
+          borderRadius: '4px',
+          marginBottom: '12px',
+          fontSize: '11px',
+          lineHeight: '1.5',
+          border: '1px solid #fbbf24',
+          whiteSpace: 'pre-wrap',
+          wordBreak: 'break-word',
+        }}>
+          {data.body_text}
+        </div>
+      )}
+
+      {/* AI分析 - 左右分栏 */}
+      {data.extraction && data.extraction.images && (
+        <div style={{
+          display: 'flex',
+          flexDirection: 'column',
+          gap: '12px',
+        }}>
+          {data.extraction.images.map((img, idx) => (
+            <div
+              key={idx}
+              style={{
+                display: 'flex',
+                flexDirection: 'row',
+                gap: '16px',
+                padding: '10px',
+                background: 'white',
+                borderRadius: '4px',
+                border: '1px solid #d97706',
+                alignItems: 'flex-start',
+              }}
+            >
+              {/* 左侧:OCR提取文字 */}
+              <div style={{
+                flex: '1',  // 1/3宽度
+                minWidth: '0',
+              }}>
+                <div style={{
+                  fontSize: '11px',
+                  fontWeight: 'bold',
+                  color: '#92400e',
+                  marginBottom: '6px',
+                }}>
+                  📝 图片 {idx + 1}/{data.extraction.images.length}
+                </div>
+
+                {img.extract_text && (
+                  <div style={{
+                    fontSize: '11px',
+                    color: '#1f2937',
+                    lineHeight: '1.6',
+                    padding: '8px',
+                    background: '#fef9e7',
+                    borderRadius: '3px',
+                    borderLeft: '3px solid #f39c12',
+                    wordBreak: 'break-word',
+                  }}>
+                    <div style={{
+                      fontSize: '10px',
+                      fontWeight: 'bold',
+                      color: '#d97706',
+                      marginBottom: '4px',
+                    }}>
+                      【提取文字】
+                    </div>
+                    {img.extract_text}
+                  </div>
+                )}
+              </div>
+
+              {/* 右侧:缩略图 + 描述 */}
+              <div style={{
+                flex: '2',  // 2/3宽度
+                display: 'flex',
+                flexDirection: 'column',
+                gap: '8px',
+                minWidth: '200px',
+              }}>
+                {/* 缩略图 */}
+                {data.image_list && data.image_list[idx] && (
+                  <img
+                    src={(data.image_list[idx].image_url || data.image_list[idx])}
+                    alt={'图片' + (idx + 1)}
+                    style={{
+                      width: '100%',
+                      height: 'auto',
+                      maxHeight: '180px',
+                      objectFit: 'contain',
+                      borderRadius: '4px',
+                      border: '1px solid #d97706',
+                      cursor: 'pointer',
+                    }}
+                    onError={(e) => {
+                      e.target.style.display = 'none';
+                    }}
+                  />
+                )}
+
+                {/* 描述文字(完整展示) */}
+                {img.description && (
+                  <div
+                    style={{
+                      fontSize: '10px',
+                      color: '#78350f',
+                      lineHeight: '1.5',
+                      wordBreak: 'break-word',
+                      padding: '8px',
+                      background: '#fef9e7',
+                      borderRadius: '3px',
+                      border: '1px solid #f39c12',
+                    }}
+                  >
+                    <div style={{
+                      fontSize: '9px',
+                      fontWeight: 'bold',
+                      color: '#d97706',
+                      marginBottom: '4px',
+                    }}>
+                      【图片描述】
+                    </div>
+                    {img.description}
+                  </div>
+                )}
+              </div>
+            </div>
+          ))}
+        </div>
+      )}
+
+      {/* 查看原帖链接 */}
+      {data.note_url && (
+        <div style={{ marginTop: '8px', fontSize: '10px' }}>
+          <a
+            href={data.note_url}
+            target="_blank"
+            rel="noopener noreferrer"
+            style={{ color: '#92400e', textDecoration: 'underline' }}
+          >
+            🔗 查看原帖
+          </a>
+        </div>
+      )}
+
+      <Handle
+        type="source"
+        position={Position.Right}
+        style={{ background: '#fbbf24', width: 8, height: 8 }}
+      />
+    </div>
+  );
+}
+
+const nodeTypes = {
+  query: QueryNode,
+  note: NoteNode,
+  post: NoteNode,  // 帖子节点使用 NoteNode 组件渲染
+  analysis: AnalysisNode,
+};
+
+// 根据 score 获取颜色
+function getScoreColor(score) {
+  if (score >= 0.7) return '#10b981'; // 绿色 - 高分
+  if (score >= 0.4) return '#f59e0b'; // 橙色 - 中分
+  return '#ef4444'; // 红色 - 低分
+}
+
+// 截断文本,保留头尾,中间显示省略号
+function truncateMiddle(text, maxLength = 20) {
+  if (!text || text.length <= maxLength) return text;
+  const headLength = Math.ceil(maxLength * 0.4);
+  const tailLength = Math.floor(maxLength * 0.4);
+  const head = text.substring(0, headLength);
+  const tail = text.substring(text.length - tailLength);
+  return `${head}...${tail}`;
+}
+
+// 根据策略获取颜色
+// 智能提取主要策略的辅助函数
+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',
+    '调用sug': '#06b6d4',
+    '同义改写': '#f59e0b',
+    '加词': '#3b82f6',
+    '抽象改写': '#8b5cf6',
+    '基于部分匹配改进': '#ec4899',
+    '结果分支-抽象改写': '#a855f7',
+    '结果分支-同义改写': '#fb923c',
+    // v6.1.2.8 新增策略
+    '原始问题': '#6b21a8',
+    '来自分词': '#10b981',
+    '加词生成': '#ef4444',
+    '建议词': '#06b6d4',
+    '执行搜索': '#8b5cf6',
+    // 添加简化版本的策略映射
+    '分词': '#10b981',
+    '推荐词': '#06b6d4',
+  };
+  return strategyColors[strategy] || '#9ca3af';
+}
+
+// 树节点组件
+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 = getPrimaryStrategy(node.data);  // 使用智能提取函数
+  const strategyColor = getStrategyColor(strategy);
+  const nodeActualType = node.data.nodeType || node.type; // 获取实际节点类型
+  const isDomainCombination = nodeActualType === 'domain_combination';
+
+  let sourceSummary = '';
+  if (isDomainCombination && Array.isArray(node.data.source_word_details) && node.data.source_word_details.length > 0) {
+    const summaryParts = [];
+    node.data.source_word_details.forEach((detail) => {
+      const words = Array.isArray(detail.words) ? detail.words : [];
+      const wordTexts = [];
+      words.forEach((w) => {
+        const numericScore = typeof w.score === 'number' ? w.score : parseFloat(w.score || '0');
+        const formattedScore = Number.isFinite(numericScore) ? numericScore.toFixed(2) : '0.00';
+        wordTexts.push(w.text + ' (' + formattedScore + ')');
+      });
+      if (wordTexts.length > 0) {
+        const segmentLabel = detail.segment_type ? '[' + detail.segment_type + '] ' : '';
+        summaryParts.push(segmentLabel + wordTexts.join(' + '));
+      }
+    });
+    sourceSummary = summaryParts.join(' | ');
+  }
+
+  // 计算字体颜色:根据分数提升幅度判断
+  let fontColor = '#374151'; // 默认颜色
+  if (node.type === 'note') {
+    fontColor = node.data.matchLevel === 'unsatisfied' ? '#ef4444' : '#374151';
+  } else if (node.data.seed_score !== undefined) {
+    const parentScore = parseFloat(node.data.seed_score);
+    const gain = score - parentScore;
+    fontColor = gain >= 0.05 ? '#16a34a' : '#ef4444';
+  } else if (node.data.isSelected === false) {
+    fontColor = '#ef4444';
+  }
+
+  return (
+    <div style={{ marginLeft: level * 12 + 'px', marginBottom: '8px' }}>
+      <div
+        style={{
+          padding: '6px 8px',
+          borderRadius: '4px',
+          cursor: 'pointer',
+          background: 'transparent',
+          border: isSelected ? '1px solid #3b82f6' : '1px solid transparent',
+          display: 'flex',
+          alignItems: 'center',
+          gap: '6px',
+          transition: 'all 0.2s ease',
+          position: 'relative',
+          overflow: 'visible',
+        }}
+        onMouseEnter={(e) => {
+          if (!isSelected) e.currentTarget.style.background = '#f9fafb';
+        }}
+        onMouseLeave={(e) => {
+          if (!isSelected) e.currentTarget.style.background = 'transparent';
+        }}
+      >
+        {/* 策略类型竖线 */}
+        <div style={{
+          width: '3px',
+          height: '20px',
+          background: strategyColor,
+          borderRadius: '2px',
+          flexShrink: 0,
+          position: 'relative',
+          zIndex: 1,
+        }} />
+
+        {hasChildren && (
+          <span
+            style={{
+              fontSize: '10px',
+              color: '#6b7280',
+              cursor: 'pointer',
+              width: '16px',
+              textAlign: 'center',
+              position: 'relative',
+              zIndex: 1,
+            }}
+            onClick={(e) => {
+              e.stopPropagation();
+              onToggle();
+            }}
+          >
+            {isCollapsed ? '▶' : '▼'}
+          </span>
+        )}
+        {!hasChildren && <span style={{ width: '16px', position: 'relative', zIndex: 1 }}></span>}
+
+        <div
+          style={{
+            flex: 1,
+            fontSize: '12px',
+            color: '#374151',
+            position: 'relative',
+            zIndex: 1,
+            minWidth: 0,
+            display: 'flex',
+            flexDirection: 'column',
+            gap: '4px',
+          }}
+          onClick={onSelect}
+        >
+          <div style={{
+            display: 'flex',
+            alignItems: 'center',
+            gap: '8px',
+          }}>
+            {/* 文本标题 - 左侧 */}
+            <div style={{
+              fontWeight: level === 0 ? '600' : '400',
+              flex: 1,
+              minWidth: 0,
+              color: node.data.scoreColor || fontColor,
+              overflow: 'hidden',
+              textOverflow: 'ellipsis',
+              whiteSpace: 'nowrap',
+            }}
+            title={node.data.title || node.id}
+            >
+              {node.data.title || node.id}
+            </div>
+
+            {/* 域标识 - 右侧,挨着分数,优先显示域类型,否则显示域索引或域字符串,但domain_combination节点不显示 */}
+            {(node.data.domain_type || node.data.domains_str || (node.data.domain_index !== null && node.data.domain_index !== undefined)) && nodeActualType !== 'domain_combination' && (
+              <span style={{
+                fontSize: '12px',
+                color: '#fff',
+                background: '#6366f1',
+                padding: '2px 5px',
+                borderRadius: '3px',
+                flexShrink: 0,
+                fontWeight: '600',
+                marginLeft: '4px',
+              }}
+              title={
+                node.data.domain_type ? '域: ' + node.data.domain_type + ' (D' + node.data.domain_index + ')' :
+                node.data.domains_str ? '域: ' + node.data.domains_str :
+                '域 D' + node.data.domain_index
+              }
+              >
+                {node.data.domain_type || node.data.domains_str || ('D' + node.data.domain_index)}
+              </span>
+            )}
+
+            {node.data.is_suggestion && node.data.suggestion_label && (
+              <span style={{
+                fontSize: '12px',
+                color: '#fff',
+                background: '#8b5cf6',
+                padding: '2px 5px',
+                borderRadius: '3px',
+                flexShrink: 0,
+                fontWeight: '600',
+              }}
+              >
+                {node.data.suggestion_label}
+              </span>
+            )}
+
+            {/* 类型标签 - 显示在右侧靠近分数,蓝色背景 */}
+            {node.data.type_label && (
+              <span style={{
+                fontSize: '12px',
+                color: '#fff',
+                background: '#6366f1',
+                padding: '2px 5px',
+                borderRadius: '3px',
+                flexShrink: 0,
+                fontWeight: '600',
+              }}
+              title={'类型: ' + node.data.type_label}
+              >
+                {node.data.type_label}
+              </span>
+            )}
+
+            {/* 分数显示 - 步骤和轮次节点不显示分数 */}
+            {nodeActualType !== 'step' && nodeActualType !== 'round' && (
+              <span style={{
+                fontSize: '11px',
+                color: '#6b7280',
+                fontWeight: '500',
+                flexShrink: 0,
+                minWidth: '35px',
+                textAlign: 'right',
+              }}>
+                {score.toFixed(2)}
+              </span>
+            )}
+          </div>
+
+          {/* 域组合的来源词得分(树状视图,右对齐) */}
+          {isDomainCombination && sourceSummary && (
+            <div style={{
+              fontSize: '10px',
+              color: '#2563eb',
+              lineHeight: '1.4',
+              display: 'flex',
+              flexDirection: 'column',
+              alignItems: 'flex-end',
+              gap: '2px',
+              textAlign: 'right',
+            }}>
+              {node.data.source_word_details.map((detail, idx) => {
+                const words = Array.isArray(detail.words) ? detail.words : [];
+                const summary = words.map((w) => {
+                  const numericScore = typeof w.score === 'number' ? w.score : parseFloat(w.score || '0');
+                  const formattedScore = Number.isFinite(numericScore) ? numericScore.toFixed(2) : '0.00';
+                  return w.text + ' (' + formattedScore + ')';
+                }).join(' + ');
+                return (
+                  <span key={idx} title={summary}>
+                    {summary}
+                  </span>
+                );
+              })}
+            </div>
+          )}
+
+          {/* 分数下划线 - 步骤和轮次节点不显示 */}
+          {nodeActualType !== 'step' && nodeActualType !== 'round' && (
+            <div style={{
+              width: (score * 100) + '%',
+              height: '2px',
+              background: getScoreColor(score),
+              borderRadius: '1px',
+            }} />
+          )}
+        </div>
+      </div>
+
+      {hasChildren && !isCollapsed && (
+        <div>
+          {children}
+        </div>
+      )}
+    </div>
+  );
+}
+
+// 使用 dagre 自动布局
+function getLayoutedElements(nodes, edges, direction = 'LR') {
+  console.log('🎯 Starting layout with dagre...');
+  console.log('Input:', nodes.length, 'nodes,', edges.length, 'edges');
+
+  // 检查 dagre 是否加载
+  if (typeof window === 'undefined' || typeof window.dagre === 'undefined') {
+    console.warn('⚠️ Dagre not loaded, using fallback layout');
+    // 降级到简单布局
+    const levelGroups = {};
+    nodes.forEach(node => {
+      const level = node.data.level || 0;
+      if (!levelGroups[level]) levelGroups[level] = [];
+      levelGroups[level].push(node);
+    });
+
+    Object.entries(levelGroups).forEach(([level, nodeList]) => {
+      const x = parseInt(level) * 480;
+      nodeList.forEach((node, index) => {
+        node.position = { x, y: index * 260 };
+        node.targetPosition = 'left';
+        node.sourcePosition = 'right';
+      });
+    });
+
+    return { nodes, edges };
+  }
+
+  try {
+    const dagreGraph = new window.dagre.graphlib.Graph();
+    dagreGraph.setDefaultEdgeLabel(() => ({}));
+
+    const isHorizontal = direction === 'LR';
+    dagreGraph.setGraph({
+      rankdir: direction,
+      nodesep: 180,   // 垂直间距 - 增加以避免节点重叠
+      ranksep: 360,  // 水平间距 - 增加以容纳更宽的节点
+    });
+
+    // 添加节点 - 根据节点类型设置不同的尺寸
+    nodes.forEach((node) => {
+      let nodeWidth = 320;
+      let nodeHeight = 220;
+
+      // note 节点有轮播图,需要更大的空间
+      if (node.type === 'note') {
+        nodeWidth = 360;
+        nodeHeight = 380;  // 增加高度以容纳轮播图
+      }
+      // analysis 节点内容很多,需要更大的空间
+      else if (node.type === 'analysis') {
+        nodeWidth = 900;   // 宽度足够容纳左右分栏
+        nodeHeight = 600;  // 高度足够容纳多张图片
+      }
+
+      dagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight });
+    });
+
+    // 添加边
+    edges.forEach((edge) => {
+      dagreGraph.setEdge(edge.source, edge.target);
+    });
+
+    // 计算布局
+    window.dagre.layout(dagreGraph);
+    console.log('✅ Dagre layout completed');
+
+    // 更新节点位置和 handle 位置
+    nodes.forEach((node) => {
+      const nodeWithPosition = dagreGraph.node(node.id);
+
+      if (!nodeWithPosition) {
+        console.warn('Node position not found for:', node.id);
+        return;
+      }
+
+      node.targetPosition = isHorizontal ? 'left' : 'top';
+      node.sourcePosition = isHorizontal ? 'right' : 'bottom';
+
+      // 根据节点类型获取尺寸
+      let nodeWidth = 320;
+      let nodeHeight = 220;
+      if (node.type === 'note') {
+        nodeWidth = 360;
+        nodeHeight = 380;
+      }
+
+      // 将 dagre 的中心点位置转换为 React Flow 的左上角位置
+      node.position = {
+        x: nodeWithPosition.x - nodeWidth / 2,
+        y: nodeWithPosition.y - nodeHeight / 2,
+      };
+    });
+
+    console.log('✅ Layout completed, sample node:', nodes[0]);
+    return { nodes, edges };
+  } catch (error) {
+    console.error('❌ Error in dagre layout:', error);
+    console.error('Error details:', error.message, error.stack);
+
+    // 降级处理
+    console.log('Using fallback layout...');
+    const levelGroups = {};
+    nodes.forEach(node => {
+      const level = node.data.level || 0;
+      if (!levelGroups[level]) levelGroups[level] = [];
+      levelGroups[level].push(node);
+    });
+
+    Object.entries(levelGroups).forEach(([level, nodeList]) => {
+      const x = parseInt(level) * 480;
+      nodeList.forEach((node, index) => {
+        node.position = { x, y: index * 260 };
+        node.targetPosition = 'left';
+        node.sourcePosition = 'right';
+      });
+    });
+
+    return { nodes, edges };
+  }
+}
+
+function transformData(data) {
+  const nodes = [];
+  const edges = [];
+
+  const originalIdToCanvasId = {}; // 原始ID -> 画布ID的映射
+  const canvasIdToNodeData = {}; // 避免重复创建相同的节点
+  let analysisNodeCount = 0; // 用于给analysis节点添加X偏移
+
+  // 创建节点
+  Object.entries(data.nodes).forEach(([originalId, node]) => {
+    // 统一处理所有类型的节点
+    const nodeType = node.type || 'query';
+
+    // 直接使用originalId作为canvasId,避免冲突
+    const canvasId = originalId;
+
+    originalIdToCanvasId[originalId] = canvasId;
+
+    // 如果这个 canvasId 还没有创建过节点,则创建
+    if (!canvasIdToNodeData[canvasId]) {
+      canvasIdToNodeData[canvasId] = true;
+
+      // 根据节点类型创建不同的数据结构
+      if (nodeType === 'note' || nodeType === 'post') {
+        nodes.push({
+          id: canvasId,
+          originalId: originalId,
+          type: 'note',
+          data: {
+            title: node.query || node.title || '帖子',
+            matchLevel: node.match_level,
+            score: node.relevance_score ? node.relevance_score.toFixed(2) : '0.00',
+            description: node.body_text || node.desc || '',
+            isSelected: node.is_selected !== undefined ? node.is_selected : true,
+            imageList: node.image_list || [],
+            noteUrl: node.note_url || '',
+            evaluationReason: node.evaluationReason || node.evaluation_reason || '',
+            interact_info: node.interact_info || {},
+            nodeType: nodeType,
+            // 🆕 添加评估字段
+            is_knowledge: node.is_knowledge !== undefined ? node.is_knowledge : null,
+            knowledge_reason: node.knowledge_reason || '',
+            post_relevance_score: node.post_relevance_score !== undefined ? node.post_relevance_score : null,
+            relevance_level: node.relevance_level || '',
+            relevance_reason: node.relevance_reason || ''
+          },
+          position: { x: 0, y: 0 },
+        });
+      } else if (nodeType === 'analysis') {
+        // AI分析节点 - 添加X偏移避免叠加
+        const xOffset = analysisNodeCount * 150; // 每个节点偏移150px
+        analysisNodeCount++;
+
+        nodes.push({
+          id: canvasId,
+          originalId: originalId,
+          type: 'analysis',
+          data: {
+            query: node.query || '[AI分析]',
+            note_id: node.note_id,
+            note_url: node.note_url,
+            title: node.title || '',
+            body_text: node.body_text || '',
+            interact_info: node.interact_info || {},
+            extraction: node.extraction || null,
+            image_list: node.image_list || [],
+          },
+          position: { x: xOffset, y: 0 },
+        });
+      } else {
+        // query, seg, q, search, root 等节点
+        let displayTitle = node.query || originalId;
+
+        nodes.push({
+          id: canvasId,
+          originalId: originalId,
+          type: 'query', // 使用 query 组件渲染所有非note节点
+          data: {
+            title: displayTitle,
+            level: node.level || 0,
+            score: node.relevance_score ? node.relevance_score.toFixed(2) : '0.00',
+            strategy: node.strategy || '',
+            parent: node.parent_query || '',
+            isSelected: node.is_selected !== undefined ? node.is_selected : true,
+            evaluationReason: node.evaluationReason || node.evaluation_reason || '',
+            nodeType: nodeType, // 传递实际节点类型用于样式
+            searchCount: node.search_count, // search 节点特有
+            totalPosts: node.total_posts, // search 节点特有
+            selectedWord: node.selected_word || '', // 加词节点特有 - 显示选择的词
+            scoreColor: node.scoreColor || null,        // SUG节点的颜色标识
+            parentQScore: node.parentQScore || 0,       // 父Q得分(用于调试)
+            domain_index: node.domain_index !== undefined ? node.domain_index : null, // 域索引
+            domain_type: node.domain_type || '', // 域类型(如"中心名词"、"核心动作"),只有Q节点有,segment节点不显示
+            segment_type: node.segment_type || '', // segment类型(只有segment节点才有)
+            type_label: node.type_label || '', // 类型标签
+            domains: node.domains || [], // 域索引数组(domain_combination节点特有)
+            domains_str: node.domains_str || '', // 域标识字符串(如"D0,D1")
+            from_segments: node.from_segments || [], // 来源segments(domain_combination节点特有)
+            source_word_details: node.source_word_details || [], // 组合来源词及其得分
+            source_scores: node.source_scores || [], // 扁平来源得分
+            is_above_sources: node.is_above_sources || false, // 组合是否高于来源得分
+            max_source_score: node.max_source_score !== undefined ? node.max_source_score : null, // 来源最高分
+            item_type: node.item_type || '', // 构建下一轮节点来源类型
+            is_suggestion: node.is_suggestion || false,
+            suggestion_label: node.suggestion_label || '',
+          },
+          position: { x: 0, y: 0 },
+        });
+      }
+    }
+  });
+
+  // 创建边 - 使用虚线样式,映射到画布ID
+  data.edges.forEach((edge, index) => {
+    const edgeColors = {
+      '初始分词': '#10b981',
+      '调用sug': '#06b6d4',
+      '同义改写': '#f59e0b',
+      '加词': '#3b82f6',
+      '抽象改写': '#8b5cf6',
+      '基于部分匹配改进': '#ec4899',
+      '结果分支-抽象改写': '#a855f7',
+      '结果分支-同义改写': '#fb923c',
+      'query_to_note': '#ec4899',
+    };
+
+    const color = edgeColors[edge.strategy] || edgeColors[edge.edge_type] || '#d1d5db';
+    const isNoteEdge = edge.edge_type === 'query_to_note';
+
+    edges.push({
+      id: `edge-${index}`,
+      source: originalIdToCanvasId[edge.from], // 使用画布ID
+      target: originalIdToCanvasId[edge.to],   // 使用画布ID
+      type: 'simplebezier', // 使用简单贝塞尔曲线
+      animated: isNoteEdge,
+      style: {
+        stroke: color,
+        strokeWidth: isNoteEdge ? 2.5 : 2,
+        strokeDasharray: isNoteEdge ? '5,5' : '8,4',
+      },
+      markerEnd: {
+        type: 'arrowclosed',
+        color: color,
+        width: 20,
+        height: 20,
+      },
+    });
+  });
+
+  // 使用 dagre 自动计算布局 - 从左到右
+  return getLayoutedElements(nodes, edges, 'LR');
+}
+
+function FlowContent() {
+  // 画布使用简化数据
+  const { nodes: initialNodes, edges: initialEdges } = useMemo(() => {
+    console.log('🔍 Transforming data for canvas...');
+    const result = transformData(data);
+    console.log('✅ Canvas data:', result.nodes.length, 'nodes,', result.edges.length, 'edges');
+    return result;
+  }, []);
+
+  // 目录使用完整数据(如果存在)
+  const { nodes: fullNodes, edges: fullEdges } = useMemo(() => {
+    if (data.fullData) {
+      console.log('🔍 Transforming full data for tree directory...');
+      const result = transformData(data.fullData);
+      console.log('✅ Directory data:', result.nodes.length, 'nodes,', result.edges.length, 'edges');
+      return result;
+    }
+    // 如果没有 fullData,使用简化数据
+    return { nodes: initialNodes, edges: initialEdges };
+  }, [initialNodes, initialEdges]);
+
+  // 初始化:找出所有有子节点的节点,默认折叠(画布节点)
+  const initialCollapsedNodes = useMemo(() => {
+    const nodesWithChildren = new Set();
+    initialEdges.forEach(edge => {
+      nodesWithChildren.add(edge.source);
+    });
+    // 排除根节点(level 0),让根节点默认展开
+    const rootNode = initialNodes.find(n => n.data.level === 0);
+    if (rootNode) {
+      nodesWithChildren.delete(rootNode.id);
+    }
+    return nodesWithChildren;
+  }, [initialNodes, initialEdges]);
+
+  // 树节点的折叠状态需要在树构建后初始化
+  const [collapsedNodes, setCollapsedNodes] = useState(() => initialCollapsedNodes);
+  const [collapsedTreeNodes, setCollapsedTreeNodes] = useState(new Set());
+  const [selectedNodeId, setSelectedNodeId] = useState(null);
+  const [hiddenNodes, setHiddenNodes] = useState(new Set()); // 用户手动隐藏的节点
+  const [focusMode, setFocusMode] = useState(false); // 全局聚焦模式,默认关闭
+  const [focusedNodeId, setFocusedNodeId] = useState(null); // 单独聚焦的节点ID
+  const [sidebarWidth, setSidebarWidth] = useState(400); // 左侧目录宽度
+  const [isResizing, setIsResizing] = useState(false); // 是否正在拖拽调整宽度
+
+  // 拖拽调整侧边栏宽度的处理逻辑
+  const handleMouseDown = useCallback(() => {
+    setIsResizing(true);
+  }, []);
+
+  useEffect(() => {
+    if (!isResizing) return;
+
+    const handleMouseMove = (e) => {
+      const newWidth = e.clientX;
+      // 限制宽度范围:300px - 700px
+      if (newWidth >= 300 && newWidth <= 700) {
+        setSidebarWidth(newWidth);
+      }
+    };
+
+    const handleMouseUp = () => {
+      setIsResizing(false);
+    };
+
+    document.addEventListener('mousemove', handleMouseMove);
+    document.addEventListener('mouseup', handleMouseUp);
+
+    return () => {
+      document.removeEventListener('mousemove', handleMouseMove);
+      document.removeEventListener('mouseup', handleMouseUp);
+    };
+  }, [isResizing]);
+
+  // 获取 React Flow 实例以控制画布
+  const { setCenter, fitView } = useReactFlow();
+
+  // 获取某个节点的所有后代节点ID
+  const getDescendants = useCallback((nodeId) => {
+    const descendants = new Set();
+    const queue = [nodeId];
+
+    while (queue.length > 0) {
+      const current = queue.shift();
+      initialEdges.forEach(edge => {
+        if (edge.source === current && !descendants.has(edge.target)) {
+          descendants.add(edge.target);
+          queue.push(edge.target);
+        }
+      });
+    }
+
+    return descendants;
+  }, [initialEdges]);
+
+  // 获取直接父节点
+  const getDirectParents = useCallback((nodeId) => {
+    const parents = [];
+    initialEdges.forEach(edge => {
+      if (edge.target === nodeId) {
+        parents.push(edge.source);
+      }
+    });
+    return parents;
+  }, [initialEdges]);
+
+  // 获取直接子节点
+  const getDirectChildren = useCallback((nodeId) => {
+    const children = [];
+    initialEdges.forEach(edge => {
+      if (edge.source === nodeId) {
+        children.push(edge.target);
+      }
+    });
+    return children;
+  }, [initialEdges]);
+
+  // 切换节点折叠状态
+  const toggleNodeCollapse = useCallback((nodeId) => {
+    setCollapsedNodes(prev => {
+      const newSet = new Set(prev);
+      const descendants = getDescendants(nodeId);
+
+      if (newSet.has(nodeId)) {
+        // 展开:移除此节点,但保持其他折叠的节点
+        newSet.delete(nodeId);
+      } else {
+        // 折叠:添加此节点
+        newSet.add(nodeId);
+      }
+
+      return newSet;
+    });
+  }, [getDescendants]);
+
+  // 过滤可见的节点和边,并重新计算布局
+  const { nodes, edges } = useMemo(() => {
+    const nodesToHide = new Set();
+
+    // 判断使用哪个节点ID进行聚焦:优先使用单独聚焦的节点,否则使用全局聚焦模式的选中节点
+    const effectiveFocusNodeId = focusedNodeId || (focusMode ? selectedNodeId : null);
+
+    // 聚焦模式:只显示聚焦节点、其父节点和直接子节点
+    if (effectiveFocusNodeId) {
+      const visibleInFocus = new Set([effectiveFocusNodeId]);
+
+      // 添加所有父节点
+      initialEdges.forEach(edge => {
+        if (edge.target === effectiveFocusNodeId) {
+          visibleInFocus.add(edge.source);
+        }
+      });
+
+      // 添加所有直接子节点
+      initialEdges.forEach(edge => {
+        if (edge.source === effectiveFocusNodeId) {
+          visibleInFocus.add(edge.target);
+        }
+      });
+
+      // 隐藏不在聚焦范围内的节点
+      initialNodes.forEach(node => {
+        if (!visibleInFocus.has(node.id)) {
+          nodesToHide.add(node.id);
+        }
+      });
+    } else {
+      // 非聚焦模式:使用原有的折叠逻辑
+      // 收集所有被折叠节点的后代
+      collapsedNodes.forEach(collapsedId => {
+        const descendants = getDescendants(collapsedId);
+        descendants.forEach(id => nodesToHide.add(id));
+      });
+    }
+
+    // 添加用户手动隐藏的节点
+    hiddenNodes.forEach(id => nodesToHide.add(id));
+
+    const visibleNodes = initialNodes
+      .filter(node => !nodesToHide.has(node.id))
+      .map(node => ({
+        ...node,
+        data: {
+          ...node.data,
+          isCollapsed: collapsedNodes.has(node.id),
+          hasChildren: initialEdges.some(e => e.source === node.id),
+          onToggleCollapse: () => toggleNodeCollapse(node.id),
+          onHideSelf: () => {
+            setHiddenNodes(prev => {
+              const newSet = new Set(prev);
+              newSet.add(node.id);
+              return newSet;
+            });
+          },
+          onFocus: () => {
+            // 切换聚焦状态
+            if (focusedNodeId === node.id) {
+              setFocusedNodeId(null); // 如果已经聚焦,则取消聚焦
+            } else {
+              // 先取消之前的聚焦,然后聚焦到当前节点
+              setFocusedNodeId(node.id);
+
+              // 延迟聚焦视图到该节点
+              setTimeout(() => {
+                fitView({
+                  nodes: [{ id: node.id }],
+                  duration: 800,
+                  padding: 0.3,
+                });
+              }, 100);
+            }
+          },
+          isFocused: focusedNodeId === node.id,
+          isHighlighted: selectedNodeId === node.id,
+        }
+      }));
+
+    const visibleEdges = initialEdges.filter(
+      edge => !nodesToHide.has(edge.source) && !nodesToHide.has(edge.target)
+    );
+
+    // 重新计算布局 - 只对可见节点
+    if (typeof window !== 'undefined' && typeof window.dagre !== 'undefined') {
+      try {
+        const dagreGraph = new window.dagre.graphlib.Graph();
+        dagreGraph.setDefaultEdgeLabel(() => ({}));
+
+        dagreGraph.setGraph({
+          rankdir: 'LR',
+          nodesep: 180,   // 垂直间距 - 增加以避免节点重叠
+          ranksep: 360,  // 水平间距 - 增加以容纳更宽的节点
+        });
+
+        visibleNodes.forEach((node) => {
+          let nodeWidth = 320;
+          let nodeHeight = 220;
+
+          // note 节点有轮播图,需要更大的空间
+          if (node.type === 'note') {
+            nodeWidth = 360;
+            nodeHeight = 380;
+          }
+
+          dagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight });
+        });
+
+        visibleEdges.forEach((edge) => {
+          dagreGraph.setEdge(edge.source, edge.target);
+        });
+
+        window.dagre.layout(dagreGraph);
+
+        visibleNodes.forEach((node) => {
+          const nodeWithPosition = dagreGraph.node(node.id);
+          if (nodeWithPosition) {
+            // 根据节点类型获取对应的尺寸
+            let nodeWidth = 320;
+            let nodeHeight = 220;
+            if (node.type === 'note') {
+              nodeWidth = 360;
+              nodeHeight = 380;
+            }
+
+            node.position = {
+              x: nodeWithPosition.x - nodeWidth / 2,
+              y: nodeWithPosition.y - nodeHeight / 2,
+            };
+            node.targetPosition = 'left';
+            node.sourcePosition = 'right';
+          }
+        });
+
+        console.log('✅ Dynamic layout recalculated for', visibleNodes.length, 'visible nodes');
+      } catch (error) {
+        console.error('❌ Error in dynamic layout:', error);
+      }
+    }
+
+    return { nodes: visibleNodes, edges: visibleEdges };
+  }, [initialNodes, initialEdges, collapsedNodes, hiddenNodes, focusMode, focusedNodeId, getDescendants, toggleNodeCollapse, selectedNodeId]);
+
+  // 构建树形结构 - 允许一个节点有多个父节点
+  // 为目录构建树(使用完整数据)
+  const buildTree = useCallback(() => {
+    // 使用完整数据构建目录树
+    const nodeMap = new Map();
+    fullNodes.forEach(node => {
+      nodeMap.set(node.id, node);
+    });
+
+    // 为每个节点创建树节点的副本(允许多次出现)
+    const createTreeNode = (nodeId, pathKey) => {
+      const node = nodeMap.get(nodeId);
+      if (!node) return null;
+
+      return {
+        ...node,
+        treeKey: pathKey, // 唯一的树路径key,用于React key
+        children: []
+      };
+    };
+
+    // 构建父子关系映射:记录每个节点的所有父节点,去重边
+    const parentToChildren = new Map();
+    const childToParents = new Map();
+
+    fullEdges.forEach(edge => {
+      // 记录父->子关系(去重:同一个父节点到同一个子节点只记录一次)
+      if (!parentToChildren.has(edge.source)) {
+        parentToChildren.set(edge.source, []);
+      }
+      const children = parentToChildren.get(edge.source);
+      if (!children.includes(edge.target)) {
+        children.push(edge.target);
+      }
+
+      // 记录子->父关系(用于判断是否有多个父节点,也去重)
+      if (!childToParents.has(edge.target)) {
+        childToParents.set(edge.target, []);
+      }
+      const parents = childToParents.get(edge.target);
+      if (!parents.includes(edge.source)) {
+        parents.push(edge.source);
+      }
+    });
+
+    // 递归构建树
+    const buildSubtree = (nodeId, pathKey, visitedInPath) => {
+      // 避免循环引用:如果当前路径中已经访问过这个节点,跳过
+      if (visitedInPath.has(nodeId)) {
+        return null;
+      }
+
+      const treeNode = createTreeNode(nodeId, pathKey);
+      if (!treeNode) return null;
+
+      const newVisitedInPath = new Set(visitedInPath);
+      newVisitedInPath.add(nodeId);
+
+      const children = parentToChildren.get(nodeId) || [];
+      treeNode.children = children
+        .map((childId, index) => buildSubtree(childId, pathKey + '-' + childId + '-' + index, newVisitedInPath))
+        .filter(child => child !== null);
+
+      return treeNode;
+    };
+
+    // 找出所有根节点(没有入边的节点)
+    const hasParent = new Set();
+    fullEdges.forEach(edge => {
+      hasParent.add(edge.target);
+    });
+
+    const roots = [];
+    fullNodes.forEach((node, index) => {
+      if (!hasParent.has(node.id)) {
+        const treeNode = buildSubtree(node.id, 'root-' + node.id + '-' + index, new Set());
+        if (treeNode) roots.push(treeNode);
+      }
+    });
+
+    return roots;
+  }, [fullNodes, fullEdges]);
+
+  const treeRoots = useMemo(() => buildTree(), [buildTree]);
+
+  // 生成树形文本结构(使用完整数据)
+  const generateTreeText = useCallback(() => {
+    const lines = [];
+
+    // 递归生成树形文本
+    const traverse = (nodes, prefix = '', isLast = true, depth = 0) => {
+      nodes.forEach((node, index) => {
+        const isLastNode = index === nodes.length - 1;
+        const nodeData = fullNodes.find(n => n.id === node.id)?.data || {};
+        const nodeType = nodeData.nodeType || node.data?.nodeType || 'unknown';
+        const title = nodeData.title || node.data?.title || node.id;
+
+        // 优先从node.data获取score,然后从nodeData获取
+        let score = null;
+        if (node.data?.score !== undefined && node.data?.score !== null) {
+          score = node.data.score;
+        } else if (node.data?.relevance_score !== undefined && node.data?.relevance_score !== null) {
+          score = node.data.relevance_score;
+        } else if (nodeData.score !== undefined && nodeData.score !== null) {
+          score = nodeData.score;
+        } else if (nodeData.relevance_score !== undefined && nodeData.relevance_score !== null) {
+          score = nodeData.relevance_score;
+        }
+
+        const strategy = nodeData.strategy || node.data?.strategy || '';
+
+        // 构建当前行 - score可能是数字或字符串,step/round节点不显示分数
+        const connector = isLastNode ? '└─' : '├─';
+        let scoreText = '';
+        if (nodeType !== 'step' && nodeType !== 'round' && score !== null && score !== undefined) {
+          // score可能已经是字符串格式(如 "0.05"),也可能是数字
+          const scoreStr = typeof score === 'number' ? score.toFixed(2) : score;
+          scoreText = ` (分数: ${scoreStr})`;
+        }
+        const strategyText = strategy ? ` [${strategy}]` : '';
+
+        lines.push(`${prefix}${connector} ${title}${scoreText}${strategyText}`);
+
+        // 递归处理子节点
+        if (node.children && node.children.length > 0) {
+          const childPrefix = prefix + (isLastNode ? '   ' : '│  ');
+          traverse(node.children, childPrefix, isLastNode, depth + 1);
+        }
+      });
+    };
+
+    // 添加标题
+    const rootNode = fullNodes.find(n => n.data?.level === 0);
+    if (rootNode) {
+      lines.push(`📊 查询扩展树形结构`);
+      lines.push(`原始问题: ${rootNode.data.title || rootNode.data.query}`);
+      lines.push('');
+    }
+
+    traverse(treeRoots);
+
+    return lines.join('\n');
+  }, [treeRoots, fullNodes]);
+
+  // 复制树形结构到剪贴板
+  const copyTreeToClipboard = useCallback(async () => {
+    try {
+      const treeText = generateTreeText();
+      await navigator.clipboard.writeText(treeText);
+      alert('✅ 树形结构已复制到剪贴板!');
+    } catch (err) {
+      console.error('复制失败:', err);
+      alert('❌ 复制失败,请手动复制');
+    }
+  }, [generateTreeText]);
+
+  // 初始化树节点折叠状态
+  useEffect(() => {
+    const getAllTreeKeys = (nodes) => {
+      const keys = new Set();
+      const traverse = (node) => {
+        if (node.children && node.children.length > 0) {
+          // 排除根节点
+          if (node.data.level !== 0) {
+            keys.add(node.treeKey);
+          }
+          node.children.forEach(traverse);
+        }
+      };
+      nodes.forEach(traverse);
+      return keys;
+    };
+
+    setCollapsedTreeNodes(getAllTreeKeys(treeRoots));
+  }, [treeRoots]);
+
+  // 映射完整节点ID到画布简化节点ID
+  const mapTreeNodeToCanvasNode = useCallback((treeNodeId) => {
+    // 如果是简化模式,需要映射
+    if (data.fullData) {
+      // 从完整数据中找到节点
+      const fullNode = fullNodes.find(n => n.id === treeNodeId);
+      if (!fullNode) return treeNodeId;
+
+      // 根据节点类型和文本找到画布上的简化节点
+      const nodeText = fullNode.data.title || fullNode.data.query;
+      const nodeType = fullNode.data.nodeType || fullNode.type;
+
+      // Query类节点:找 query_xxx
+      if (['q', 'seg', 'sug', 'add_word', 'query'].includes(nodeType)) {
+        const canvasNode = initialNodes.find(n =>
+          (n.data.title === nodeText || n.data.query === nodeText) &&
+          ['query'].includes(n.type)
+        );
+        return canvasNode ? canvasNode.id : treeNodeId;
+      }
+
+      // Post节点:按note_id查找
+      if (nodeType === 'post' || nodeType === 'note') {
+        const noteId = fullNode.data.note_id;
+        if (noteId) {
+          const canvasNode = initialNodes.find(n => n.data.note_id === noteId);
+          return canvasNode ? canvasNode.id : treeNodeId;
+        }
+      }
+
+      // 其他节点类型(Round/Step等):直接返回
+      return treeNodeId;
+    }
+
+    // 非简化模式,直接返回
+    return treeNodeId;
+  }, [data.fullData, fullNodes, initialNodes]);
+
+  const renderTree = useCallback((treeNodes, level = 0) => {
+    return treeNodes.map(node => {
+      // 使用 treeKey 来区分树中的不同实例
+      const isCollapsed = collapsedTreeNodes.has(node.treeKey);
+      const isSelected = selectedNodeId === node.id;
+
+      return (
+        <TreeNode
+          key={node.treeKey}
+          node={node}
+          level={level}
+          isCollapsed={isCollapsed}
+          isSelected={isSelected}
+          onToggle={() => {
+            setCollapsedTreeNodes(prev => {
+              const newSet = new Set(prev);
+              if (newSet.has(node.treeKey)) {
+                newSet.delete(node.treeKey);
+              } else {
+                newSet.add(node.treeKey);
+              }
+              return newSet;
+            });
+          }}
+          onSelect={() => {
+            // 将目录节点ID映射到画布节点ID
+            const treeNodeId = node.id;
+            const canvasNodeId = mapTreeNodeToCanvasNode(treeNodeId);
+
+            // 检查画布上是否存在这个节点
+            const canvasNodeExists = initialNodes.some(n => n.id === canvasNodeId);
+            if (!canvasNodeExists) {
+              console.warn(`节点 ${canvasNodeId} 在画布上不存在(可能被简化了)`);
+              return;
+            }
+
+            const nodeId = canvasNodeId;
+
+            // 展开所有祖先节点
+            const ancestorIds = [nodeId];
+            const findAncestors = (id) => {
+              initialEdges.forEach(edge => {
+                if (edge.target === id && !ancestorIds.includes(edge.source)) {
+                  ancestorIds.push(edge.source);
+                  findAncestors(edge.source);
+                }
+              });
+            };
+            findAncestors(nodeId);
+
+            // 如果节点或其祖先被隐藏,先恢复它们
+            setHiddenNodes(prev => {
+              const newSet = new Set(prev);
+              ancestorIds.forEach(id => newSet.delete(id));
+              return newSet;
+            });
+
+            setSelectedNodeId(nodeId);
+
+            // 获取选中节点的直接子节点
+            const childrenIds = [];
+            initialEdges.forEach(edge => {
+              if (edge.source === nodeId) {
+                childrenIds.push(edge.target);
+              }
+            });
+
+            setCollapsedNodes(prev => {
+              const newSet = new Set(prev);
+              // 展开所有祖先节点
+              ancestorIds.forEach(id => newSet.delete(id));
+              // 展开选中节点本身
+              newSet.delete(nodeId);
+              // 展开选中节点的直接子节点
+              childrenIds.forEach(id => newSet.delete(id));
+              return newSet;
+            });
+
+            // 延迟聚焦,等待节点展开和布局重新计算
+            setTimeout(() => {
+              fitView({
+                nodes: [{ id: nodeId }],
+                duration: 800,
+                padding: 0.3,
+              });
+            }, 300);
+          }}
+        >
+          {node.children && node.children.length > 0 && renderTree(node.children, level + 1)}
+        </TreeNode>
+      );
+    });
+  }, [collapsedTreeNodes, selectedNodeId, nodes, setCenter, initialEdges, setCollapsedNodes, fitView, mapTreeNodeToCanvasNode, initialNodes, setHiddenNodes]);
+
+  console.log('📊 Rendering with', nodes.length, 'visible nodes and', edges.length, 'visible edges');
+
+  if (nodes.length === 0) {
+    return (
+      <div style={{ padding: 50, color: 'red', fontSize: 20 }}>
+        ERROR: No nodes to display!
+      </div>
+    );
+  }
+
+  return (
+    <div style={{ width: '100vw', height: '100vh', background: '#f9fafb', display: 'flex', flexDirection: 'column' }}>
+      {/* 顶部面包屑导航栏 */}
+      <div style={{
+        minHeight: '48px',
+        maxHeight: '120px',
+        background: 'white',
+        borderBottom: '1px solid #e5e7eb',
+        display: 'flex',
+        alignItems: 'flex-start',
+        padding: '12px 24px',
+        zIndex: 1000,
+        boxShadow: '0 1px 3px rgba(0, 0, 0, 0.05)',
+        flexShrink: 0,
+        overflowY: 'auto',
+      }}>
+        <div style={{ width: '100%' }}>
+          {selectedNodeId ? (
+            <div style={{ fontSize: '12px', color: '#6b7280' }}>
+              {/* 面包屑导航 - 显示所有路径 */}
+              {(() => {
+                const selectedNode = nodes.find(n => n.id === selectedNodeId);
+                if (!selectedNode) return null;
+
+                // 找到所有从根节点到当前节点的路径
+                const findAllPaths = (targetId) => {
+                  const paths = [];
+
+                  const buildPath = (nodeId, currentPath) => {
+                    const node = initialNodes.find(n => n.id === nodeId);
+                    if (!node) return;
+
+                    const newPath = [node, ...currentPath];
+
+                    // 找到所有父节点
+                    const parents = initialEdges.filter(e => e.target === nodeId).map(e => e.source);
+
+                    if (parents.length === 0) {
+                      // 到达根节点
+                      paths.push(newPath);
+                    } else {
+                      // 递归处理所有父节点
+                      parents.forEach(parentId => {
+                        buildPath(parentId, newPath);
+                      });
+                    }
+                  };
+
+                  buildPath(targetId, []);
+                  return paths;
+                };
+
+                const allPaths = findAllPaths(selectedNodeId);
+
+                // 去重:将路径转换为字符串进行比较
+                const uniquePaths = [];
+                const pathStrings = new Set();
+                allPaths.forEach(path => {
+                  const pathString = path.map(n => n.id).join('->');
+                  if (!pathStrings.has(pathString)) {
+                    pathStrings.add(pathString);
+                    uniquePaths.push(path);
+                  }
+                });
+
+                return (
+                  <div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>
+                    {uniquePaths.map((path, pathIndex) => (
+                      <div key={pathIndex} style={{ display: 'flex', alignItems: 'center', gap: '6px', flexWrap: 'wrap' }}>
+                        {pathIndex > 0 && <span style={{ color: '#d1d5db', marginRight: '4px' }}>或</span>}
+                        {path.map((node, index) => {
+                          // 获取节点的 score、strategy 和 isSelected
+                          const nodeScore = node.data.score ? parseFloat(node.data.score) : 0;
+                          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; // 获取实际节点类型
+
+                          // 计算路径节点字体颜色:根据分数提升幅度判断
+                          let pathFontColor = '#374151'; // 默认颜色
+                          if (node.type === 'note') {
+                            pathFontColor = node.data.matchLevel === 'unsatisfied' ? '#ef4444' : '#374151';
+                          } else if (node.data.seed_score !== undefined) {
+                            const parentScore = parseFloat(node.data.seed_score);
+                            const gain = nodeScore - parentScore;
+                            pathFontColor = gain >= 0.05 ? '#16a34a' : '#ef4444';
+                          } else if (index > 0) {
+                            const prevNode = path[index - 1];
+                            const prevScore = prevNode.data.score ? parseFloat(prevNode.data.score) : 0;
+                            const gain = nodeScore - prevScore;
+                            pathFontColor = gain >= 0.05 ? '#16a34a' : '#ef4444';
+                          } else if (node.data.isSelected === false) {
+                            pathFontColor = '#ef4444';
+                          }
+
+                          return (
+                          <React.Fragment key={node.id + '-' + index}>
+                            <span
+                              onClick={() => {
+                                const nodeId = node.id;
+
+                                // 找到所有祖先节点
+                                const ancestorIds = [nodeId];
+                                const findAncestors = (id) => {
+                                  initialEdges.forEach(edge => {
+                                    if (edge.target === id && !ancestorIds.includes(edge.source)) {
+                                      ancestorIds.push(edge.source);
+                                      findAncestors(edge.source);
+                                    }
+                                  });
+                                };
+                                findAncestors(nodeId);
+
+                                // 如果节点或其祖先被隐藏,先恢复它们
+                                setHiddenNodes(prev => {
+                                  const newSet = new Set(prev);
+                                  ancestorIds.forEach(id => newSet.delete(id));
+                                  return newSet;
+                                });
+
+                                // 展开目录树中到达该节点的路径
+                                // 需要找到所有包含该节点的树路径的 treeKey,并展开它们的父节点
+                                setCollapsedTreeNodes(prev => {
+                                  const newSet = new Set(prev);
+                                  // 清空所有折叠状态,让目录树完全展开到选中节点
+                                  // 这样可以确保选中节点在目录中可见
+                                  return new Set();
+                                });
+
+                                setSelectedNodeId(nodeId);
+                                setTimeout(() => {
+                                  fitView({
+                                    nodes: [{ id: nodeId }],
+                                    duration: 800,
+                                    padding: 0.3,
+                                  });
+                                }, 100);
+                              }}
+                              style={{
+                                padding: '6px 8px',
+                                borderRadius: '4px',
+                                background: 'white',
+                                border: index === path.length - 1 ? '2px solid #3b82f6' : '1px solid #d1d5db',
+                                color: '#374151',
+                                fontWeight: index === path.length - 1 ? '600' : '400',
+                                width: '180px',
+                                cursor: 'pointer',
+                                transition: 'all 0.2s ease',
+                                position: 'relative',
+                                display: 'inline-flex',
+                                flexDirection: 'column',
+                                gap: '4px',
+                              }}
+                              onMouseEnter={(e) => {
+                                e.currentTarget.style.opacity = '0.8';
+                              }}
+                              onMouseLeave={(e) => {
+                                e.currentTarget.style.opacity = '1';
+                              }}
+                              title={`${node.data.title || node.id} (Score: ${nodeScore.toFixed(2)}, Strategy: ${nodeStrategy}, Selected: ${nodeIsSelected})`}
+                            >
+                              {/* 上半部分:竖线 + 图标 + 文字 + 分数 */}
+                              <div style={{
+                                display: 'flex',
+                                alignItems: 'center',
+                                gap: '6px',
+                              }}>
+                                {/* 策略类型竖线 */}
+                                <div style={{
+                                  width: '3px',
+                                  height: '16px',
+                                  background: strategyColor,
+                                  borderRadius: '2px',
+                                  flexShrink: 0,
+                                }} />
+
+                                {/* 节点文字 - 左侧 */}
+                                <span style={{
+                                  flex: 1,
+                                  fontSize: '12px',
+                                  color: pathFontColor,
+                                  overflow: 'hidden',
+                                  textOverflow: 'ellipsis',
+                                  whiteSpace: 'nowrap',
+                                }}>
+                                  {node.data.title || node.id}
+                                </span>
+
+                                {/* 域标识 - 右侧,挨着分数 */}
+                                {(node.data.domain_type || node.data.domains_str || (node.data.domain_index !== null && node.data.domain_index !== undefined)) && (
+                                  <span style={{
+                                    fontSize: '12px',
+                                    color: '#fff',
+                                    background: '#6366f1',
+                                    padding: '2px 5px',
+                                    borderRadius: '3px',
+                                    flexShrink: 0,
+                                    fontWeight: '600',
+                                    marginLeft: '4px',
+                                  }}
+                                  title={
+                                    node.data.domain_type ? '域: ' + node.data.domain_type + ' (D' + node.data.domain_index + ')' :
+                                    node.data.domains_str ? '域: ' + node.data.domains_str :
+                                    '域 D' + node.data.domain_index
+                                  }
+                                  >
+                                    {node.data.domain_type || node.data.domains_str || ('D' + node.data.domain_index)}
+                                  </span>
+                                )}
+
+                                {/* 分数显示 - 步骤和轮次节点不显示分数 */}
+                                {nodeActualType !== 'step' && nodeActualType !== 'round' && (
+                                  <span style={{
+                                    fontSize: '10px',
+                                    color: '#6b7280',
+                                    fontWeight: '500',
+                                    flexShrink: 0,
+                                    minWidth: '35px',
+                                    textAlign: 'right',
+                                    marginLeft: '4px',
+                                  }}>
+                                    {nodeScore.toFixed(2)}
+                                  </span>
+                                )}
+                              </div>
+
+                              {/* 分数下划线 - 步骤和轮次节点不显示 */}
+                              {nodeActualType !== 'step' && nodeActualType !== 'round' && (
+                                <div style={{
+                                  width: (nodeScore * 100) + '%',
+                                  height: '2px',
+                                  background: getScoreColor(nodeScore),
+                                  borderRadius: '1px',
+                                  marginLeft: '9px',
+                                }} />
+                              )}
+                            </span>
+                            {index < path.length - 1 && <span style={{ color: '#9ca3af' }}>›</span>}
+                          </React.Fragment>
+                        )})}
+                      </div>
+                    ))}
+                  </div>
+                );
+              })()}
+            </div>
+          ) : (
+            <div style={{ fontSize: '13px', color: '#9ca3af', textAlign: 'center' }}>
+              选择一个节点查看路径
+            </div>
+          )}
+        </div>
+      </div>
+
+      {/* 主内容区:目录 + 画布 */}
+      <div style={{
+        display: 'flex',
+        flex: 1,
+        overflow: 'hidden',
+        cursor: isResizing ? 'col-resize' : 'default',
+        userSelect: isResizing ? 'none' : 'auto',
+      }}>
+        {/* 左侧目录树 */}
+        <div style={{
+          width: `${sidebarWidth}px`,
+          background: 'white',
+          borderRight: '1px solid #e5e7eb',
+          display: 'flex',
+          flexDirection: 'column',
+          flexShrink: 0,
+        }}>
+          <div style={{
+            padding: '12px 16px',
+            borderBottom: '1px solid #e5e7eb',
+            display: 'flex',
+            justifyContent: 'space-between',
+            alignItems: 'center',
+          }}>
+            <span style={{
+              fontWeight: '600',
+              fontSize: '14px',
+              color: '#111827',
+            }}>
+              节点目录
+            </span>
+            <div style={{ display: 'flex', gap: '6px' }}>
+              <button
+                onClick={() => {
+                  setCollapsedTreeNodes(new Set());
+                }}
+                style={{
+                  fontSize: '11px',
+                  padding: '4px 8px',
+                  borderRadius: '4px',
+                  border: '1px solid #d1d5db',
+                  background: 'white',
+                  color: '#6b7280',
+                  cursor: 'pointer',
+                  fontWeight: '500',
+                }}
+                title="展开全部节点"
+              >
+                全部展开
+              </button>
+              <button
+                onClick={() => {
+                  const getAllTreeKeys = (nodes) => {
+                    const keys = new Set();
+                    const traverse = (node) => {
+                      if (node.children && node.children.length > 0) {
+                        keys.add(node.treeKey);
+                        node.children.forEach(traverse);
+                      }
+                    };
+                    nodes.forEach(traverse);
+                    return keys;
+                  };
+                  setCollapsedTreeNodes(getAllTreeKeys(treeRoots));
+                }}
+                style={{
+                  fontSize: '11px',
+                  padding: '4px 8px',
+                  borderRadius: '4px',
+                  border: '1px solid #d1d5db',
+                  background: 'white',
+                  color: '#6b7280',
+                  cursor: 'pointer',
+                  fontWeight: '500',
+                }}
+                title="折叠全部节点"
+              >
+                全部折叠
+              </button>
+              <button
+                onClick={copyTreeToClipboard}
+                style={{
+                  fontSize: '11px',
+                  padding: '4px 8px',
+                  borderRadius: '4px',
+                  border: '1px solid #3b82f6',
+                  background: '#3b82f6',
+                  color: 'white',
+                  cursor: 'pointer',
+                  fontWeight: '500',
+                  transition: 'all 0.2s',
+                }}
+                onMouseEnter={(e) => e.currentTarget.style.background = '#2563eb'}
+                onMouseLeave={(e) => e.currentTarget.style.background = '#3b82f6'}
+                title="复制树形结构为文本格式"
+              >
+                📋 复制树形结构
+              </button>
+            </div>
+          </div>
+          <div style={{
+            flex: 1,
+            overflowX: 'auto',
+            overflowY: 'auto',
+            padding: '8px',
+          }}>
+            <div style={{ minWidth: 'fit-content' }}>
+              {renderTree(treeRoots)}
+            </div>
+          </div>
+        </div>
+
+        {/* 可拖拽的分隔条 */}
+        <div
+          onMouseDown={handleMouseDown}
+          style={{
+            width: '4px',
+            cursor: 'col-resize',
+            background: isResizing ? '#3b82f6' : 'transparent',
+            transition: isResizing ? 'none' : 'background 0.2s',
+            flexShrink: 0,
+            position: 'relative',
+          }}
+          onMouseEnter={(e) => e.currentTarget.style.background = '#e5e7eb'}
+          onMouseLeave={(e) => {
+            if (!isResizing) e.currentTarget.style.background = 'transparent';
+          }}
+        >
+          {/* 拖拽提示线 */}
+          <div style={{
+            position: 'absolute',
+            top: '50%',
+            left: '50%',
+            transform: 'translate(-50%, -50%)',
+            width: '1px',
+            height: '40px',
+            background: '#9ca3af',
+            opacity: isResizing ? 1 : 0.3,
+          }} />
+        </div>
+
+        {/* 画布区域 */}
+        <div style={{ flex: 1, position: 'relative' }}>
+
+          {/* 右侧图例 */}
+          <div style={{
+            position: 'absolute',
+            top: '20px',
+            right: '20px',
+            background: 'white',
+            padding: '16px',
+            borderRadius: '12px',
+            boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08)',
+            zIndex: 1000,
+            maxWidth: '260px',
+            border: '1px solid #e5e7eb',
+          }}>
+        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '12px' }}>
+          <h3 style={{ fontSize: '14px', fontWeight: '600', color: '#111827', margin: 0 }}>图例</h3>
+          <button
+            onClick={() => setFocusMode(!focusMode)}
+            style={{
+              fontSize: '11px',
+              padding: '4px 8px',
+              borderRadius: '4px',
+              border: '1px solid',
+              borderColor: focusMode ? '#3b82f6' : '#d1d5db',
+              background: focusMode ? '#3b82f6' : 'white',
+              color: focusMode ? 'white' : '#6b7280',
+              cursor: 'pointer',
+              fontWeight: '500',
+            }}
+            title={focusMode ? '关闭聚焦模式' : '开启聚焦模式'}
+          >
+            {focusMode ? '🎯 聚焦' : '📊 全图'}
+          </button>
+        </div>
+
+        <div style={{ fontSize: '12px' }}>
+          {/* 画布节点展开/折叠控制 */}
+          <div style={{ marginBottom: '12px', paddingBottom: '12px', borderBottom: '1px solid #f3f4f6' }}>
+            <div style={{ fontSize: '12px', fontWeight: '500', marginBottom: '8px', color: '#374151' }}>节点控制</div>
+            <div style={{ display: 'flex', gap: '6px' }}>
+              <button
+                onClick={() => {
+                  setCollapsedNodes(new Set());
+                }}
+                style={{
+                  fontSize: '11px',
+                  padding: '4px 8px',
+                  borderRadius: '4px',
+                  border: '1px solid #d1d5db',
+                  background: 'white',
+                  color: '#6b7280',
+                  cursor: 'pointer',
+                  fontWeight: '500',
+                  flex: 1,
+                }}
+                title="展开画布中所有节点的子节点"
+              >
+                全部展开
+              </button>
+              <button
+                onClick={() => {
+                  const allNodeIds = new Set(initialNodes.map(n => n.id));
+                  setCollapsedNodes(allNodeIds);
+                }}
+                style={{
+                  fontSize: '11px',
+                  padding: '4px 8px',
+                  borderRadius: '4px',
+                  border: '1px solid #d1d5db',
+                  background: 'white',
+                  color: '#6b7280',
+                  cursor: 'pointer',
+                  fontWeight: '500',
+                  flex: 1,
+                }}
+                title="折叠画布中所有节点的子节点"
+              >
+                全部折叠
+              </button>
+            </div>
+          </div>
+
+          <div style={{ paddingTop: '12px', borderTop: '1px solid #f3f4f6' }}>
+            <div style={{ fontSize: '12px', fontWeight: '500', marginBottom: '8px', color: '#374151' }}>策略类型</div>
+            <div style={{ display: 'flex', alignItems: 'center', margin: '6px 0' }}>
+              <div style={{ width: '20px', height: '2px', marginRight: '8px', background: '#10b981', opacity: 0.7 }}></div>
+              <span style={{ color: '#6b7280', fontSize: '11px' }}>初始分词</span>
+            </div>
+            <div style={{ display: 'flex', alignItems: 'center', margin: '6px 0' }}>
+              <div style={{ width: '20px', height: '2px', marginRight: '8px', background: '#06b6d4', opacity: 0.7 }}></div>
+              <span style={{ color: '#6b7280', fontSize: '11px' }}>调用sug</span>
+            </div>
+            <div style={{ display: 'flex', alignItems: 'center', margin: '6px 0' }}>
+              <div style={{ width: '20px', height: '2px', marginRight: '8px', background: '#f59e0b', opacity: 0.7 }}></div>
+              <span style={{ color: '#6b7280', fontSize: '11px' }}>同义改写</span>
+            </div>
+            <div style={{ display: 'flex', alignItems: 'center', margin: '6px 0' }}>
+              <div style={{ width: '20px', height: '2px', marginRight: '8px', background: '#3b82f6', opacity: 0.7 }}></div>
+              <span style={{ color: '#6b7280', fontSize: '11px' }}>加词</span>
+            </div>
+            <div style={{ display: 'flex', alignItems: 'center', margin: '6px 0' }}>
+              <div style={{ width: '20px', height: '2px', marginRight: '8px', background: '#8b5cf6', opacity: 0.7 }}></div>
+              <span style={{ color: '#6b7280', fontSize: '11px' }}>抽象改写</span>
+            </div>
+            <div style={{ display: 'flex', alignItems: 'center', margin: '6px 0' }}>
+              <div style={{ width: '20px', height: '2px', marginRight: '8px', background: '#ec4899', opacity: 0.7 }}></div>
+              <span style={{ color: '#6b7280', fontSize: '11px' }}>基于部分匹配改进</span>
+            </div>
+            <div style={{ display: 'flex', alignItems: 'center', margin: '6px 0' }}>
+              <div style={{ width: '20px', height: '2px', marginRight: '8px', background: '#a855f7', opacity: 0.7 }}></div>
+              <span style={{ color: '#6b7280', fontSize: '11px' }}>结果分支-抽象改写</span>
+            </div>
+            <div style={{ display: 'flex', alignItems: 'center', margin: '6px 0' }}>
+              <div style={{ width: '20px', height: '2px', marginRight: '8px', background: '#fb923c', opacity: 0.7 }}></div>
+              <span style={{ color: '#6b7280', fontSize: '11px' }}>结果分支-同义改写</span>
+            </div>
+          </div>
+
+          <div style={{
+            marginTop: '12px',
+            paddingTop: '12px',
+            borderTop: '1px solid #f3f4f6',
+            fontSize: '11px',
+            color: '#9ca3af',
+            lineHeight: '1.5',
+          }}>
+            💡 点击节点左上角 × 隐藏节点
+          </div>
+
+          {/* 隐藏节点列表 - 在图例内部 */}
+          {hiddenNodes.size > 0 && (
+            <div style={{
+              marginTop: '12px',
+              paddingTop: '12px',
+              borderTop: '1px solid #f3f4f6',
+            }}>
+              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '8px' }}>
+                <h4 style={{ fontSize: '12px', fontWeight: '600', color: '#111827' }}>已隐藏节点</h4>
+                <button
+                  onClick={() => setHiddenNodes(new Set())}
+                  style={{
+                    fontSize: '10px',
+                    color: '#3b82f6',
+                    background: 'none',
+                    border: 'none',
+                    cursor: 'pointer',
+                    textDecoration: 'underline',
+                  }}
+                >
+                  全部恢复
+                </button>
+              </div>
+              <div style={{ fontSize: '12px', maxHeight: '200px', overflow: 'auto' }}>
+                {Array.from(hiddenNodes).map(nodeId => {
+                  const node = initialNodes.find(n => n.id === nodeId);
+                  if (!node) return null;
+                  return (
+                    <div
+                      key={nodeId}
+                      style={{
+                        display: 'flex',
+                        justifyContent: 'space-between',
+                        alignItems: 'center',
+                        padding: '6px 8px',
+                        margin: '4px 0',
+                        background: '#f9fafb',
+                        borderRadius: '6px',
+                        fontSize: '11px',
+                      }}
+                    >
+                      <span
+                        style={{
+                          flex: 1,
+                          overflow: 'hidden',
+                          textOverflow: 'ellipsis',
+                          whiteSpace: 'nowrap',
+                          color: '#374151',
+                        }}
+                        title={node.data.title || nodeId}
+                      >
+                        {node.data.title || nodeId}
+                      </span>
+                      <button
+                        onClick={() => {
+                          setHiddenNodes(prev => {
+                            const newSet = new Set(prev);
+                            newSet.delete(nodeId);
+                            return newSet;
+                          });
+                        }}
+                        style={{
+                          marginLeft: '8px',
+                          fontSize: '10px',
+                          color: '#10b981',
+                          background: 'none',
+                          border: 'none',
+                          cursor: 'pointer',
+                          flexShrink: 0,
+                        }}
+                      >
+                        恢复
+                      </button>
+                    </div>
+                  );
+                })}
+              </div>
+            </div>
+          )}
+          </div>
+          </div>
+
+          {/* React Flow 画布 */}
+          <ReactFlow
+            nodes={nodes}
+            edges={edges}
+            nodeTypes={nodeTypes}
+            fitView
+            fitViewOptions={{ padding: 0.2, duration: 500 }}
+            minZoom={0.1}
+            maxZoom={1.5}
+            nodesDraggable={true}
+            nodesConnectable={false}
+            elementsSelectable={true}
+            defaultEdgeOptions={{
+              type: 'smoothstep',
+            }}
+            proOptions={{ hideAttribution: true }}
+            onNodeClick={(event, clickedNode) => {
+              setSelectedNodeId(clickedNode.id);
+            }}
+          >
+            <Controls style={{ bottom: '20px', left: 'auto', right: '20px' }} />
+            <Background variant="dots" gap={20} size={1} color="#e5e7eb" />
+          </ReactFlow>
+        </div>
+      </div>
+    </div>
+  );
+}
+
+function App() {
+  return (
+    <ReactFlowProvider>
+      <FlowContent />
+    </ReactFlowProvider>
+  );
+}
+
+const root = createRoot(document.getElementById('root'));
+root.render(<App />);

BIN
visualization/knowledge_search_traverse/image.png


+ 145 - 4
visualization/knowledge_search_traverse/index.js

@@ -3,7 +3,7 @@
 const fs = require('fs');
 const path = require('path');
 const { build } = require('esbuild');
-const { convertV8ToGraph } = require('./convert_v8_to_graph');
+// const { convertV8ToGraph } = require('./convert_v8_to_graph'); // 已废弃,使用v3版本
 const { convertV8ToGraphV2, convertV8ToGraphSimplified } = require('./convert_v8_to_graph_v3');
 
 // 读取命令行参数
@@ -546,6 +546,87 @@ function NoteNode({ id, data, sourcePosition, targetPosition }) {
           </div>
         </div>
 
+        {/* 评估信息区域 */}
+        {(data.is_knowledge !== undefined && data.is_knowledge !== null || data.post_relevance_score !== undefined && data.post_relevance_score !== null) && (
+          <div style={{
+            marginBottom: '10px',
+            paddingBottom: '8px',
+            borderBottom: '1px solid #fce7f3',
+          }}>
+            {/* 知识判定标签 */}
+            {(data.is_knowledge !== undefined && data.is_knowledge !== null) && (
+              <div style={{ marginBottom: '8px' }}>
+                <span style={{
+                  display: 'inline-block',
+                  padding: '3px 10px',
+                  borderRadius: '12px',
+                  fontSize: '11px',
+                  fontWeight: '600',
+                  background: data.is_knowledge ? '#dcfce7' : '#fee2e2',
+                  color: data.is_knowledge ? '#166534' : '#991b1b',
+                }}>
+                  {data.is_knowledge ? '✓ 知识内容' : '✗ 非知识'}
+                </span>
+                {data.knowledge_reason && (
+                  <div style={{
+                    marginTop: '4px',
+                    fontSize: '10px',
+                    color: '#9f1239',
+                    lineHeight: '1.4',
+                  }}>
+                    {data.knowledge_reason}
+                  </div>
+                )}
+              </div>
+            )}
+
+            {/* 相关性得分 */}
+            {(data.post_relevance_score !== undefined && data.post_relevance_score !== null) && (
+              <div>
+                <div style={{
+                  display: 'flex',
+                  alignItems: 'center',
+                  gap: '6px',
+                  marginBottom: '4px',
+                }}>
+                  <span style={{
+                    fontSize: '11px',
+                    fontWeight: '600',
+                    color: '#9f1239',
+                  }}>
+                    相关性: {(data.post_relevance_score * 100).toFixed(0)}%
+                  </span>
+                  {data.relevance_level && (
+                    <span style={{
+                      padding: '2px 8px',
+                      borderRadius: '10px',
+                      fontSize: '10px',
+                      fontWeight: '600',
+                      background:
+                        data.relevance_level === '高度相关' ? '#dcfce7' :
+                        data.relevance_level === '中度相关' ? '#fef3c7' : '#fee2e2',
+                      color:
+                        data.relevance_level === '高度相关' ? '#166534' :
+                        data.relevance_level === '中度相关' ? '#854d0e' : '#991b1b',
+                    }}>
+                      {data.relevance_level}
+                    </span>
+                  )}
+                </div>
+                {data.relevance_reason && (
+                  <div style={{
+                    fontSize: '10px',
+                    color: '#9f1239',
+                    lineHeight: '1.4',
+                  }}>
+                    {data.relevance_reason}
+                  </div>
+                )}
+              </div>
+            )}
+          </div>
+        )}
+
         {/* 轮播图 */}
         {hasImages && (
           <div style={{
@@ -983,6 +1064,7 @@ function AnalysisNode({ data }) {
 const nodeTypes = {
   query: QueryNode,
   note: NoteNode,
+  post: NoteNode,  // 帖子节点使用 NoteNode 组件渲染
   analysis: AnalysisNode,
 };
 
@@ -1462,6 +1544,12 @@ function transformData(data) {
             evaluationReason: node.evaluationReason || node.evaluation_reason || '',
             interact_info: node.interact_info || {},
             nodeType: nodeType,
+            // 🆕 添加评估字段
+            is_knowledge: node.is_knowledge !== undefined ? node.is_knowledge : null,
+            knowledge_reason: node.knowledge_reason || '',
+            post_relevance_score: node.post_relevance_score !== undefined ? node.post_relevance_score : null,
+            relevance_level: node.relevance_level || '',
+            relevance_reason: node.relevance_reason || ''
           },
           position: { x: 0, y: 0 },
         });
@@ -2820,6 +2908,10 @@ root.render(<App />);
 
 fs.writeFileSync(reactComponentPath, reactComponent);
 
+// 调试:保存临时组件副本用于检查
+fs.writeFileSync(path.join(__dirname, 'debug_component.jsx'), reactComponent);
+console.log('📝 已保存临时组件副本: debug_component.jsx');
+
 // 使用 esbuild 打包
 console.log('🎨 Building modern visualization...');
 
@@ -2832,6 +2924,9 @@ build({
     '.css': 'css',
   },
   minify: false,
+  treeShaking: false, // 禁用tree shaking
+  ignoreAnnotations: true, // 忽略所有注解,防止纯函数优化
+  keepNames: true, // 保留函数和变量名
   sourcemap: 'inline',
   // 强制所有 React 引用指向同一个位置,避免多副本
   alias: {
@@ -2840,10 +2935,25 @@ build({
     'react/jsx-runtime': path.join(__dirname, 'node_modules/react/jsx-runtime'),
     'react/jsx-dev-runtime': path.join(__dirname, 'node_modules/react/jsx-dev-runtime'),
   },
+  define: {
+    'process.env.NODE_ENV': '"development"' // 使用开发模式,减少优化
+  },
 }).then(() => {
   // 读取打包后的 JS
   const bundleJs = fs.readFileSync(path.join(__dirname, 'bundle_v2.js'), 'utf-8');
 
+  // 调试:检查bundle中是否包含评估UI代码
+  const hasEvalCode = bundleJs.includes('知识内容') || bundleJs.includes('is_knowledge');
+  console.log('📝 Bundle调试: 包含评估代码 =', hasEvalCode);
+  if (hasEvalCode) {
+    console.log('  ✓ 评估UI代码在bundle中');
+  } else {
+    console.log('  ⚠️  评估UI代码不在bundle中,检查临时组件文件...');
+    const tempContent = fs.readFileSync(reactComponentPath, 'utf-8');
+    const hasTempEvalCode = tempContent.includes('知识内容');
+    console.log('  临时组件文件包含评估代码 =', hasTempEvalCode);
+  }
+
   // 读取 CSS
   const cssPath = path.join(__dirname, 'node_modules/@xyflow/react/dist/style.css');
   const css = fs.readFileSync(cssPath, 'utf-8');
@@ -2911,12 +3021,43 @@ build({
 </body>
 </html>`;
 
+  // 调试:详细检查bundle和HTML内容
+  const bundleHas知识 = bundleJs.includes('知识内容');
+  const bundleHasIsKnowledge = bundleJs.includes('is_knowledge');
+  const bundleHasDataIsKnowledge = bundleJs.includes('data.is_knowledge');
+
+  console.log('📝 Bundle内容检查:');
+  console.log('  包含 "知识内容":', bundleHas知识);
+  console.log('  包含 "is_knowledge":', bundleHasIsKnowledge);
+  console.log('  包含 "data.is_knowledge":', bundleHasDataIsKnowledge);
+  console.log('  Bundle长度:', bundleJs.length);
+
+  const htmlHas知识 = html.includes('知识内容');
+  const htmlHasIsKnowledge = html.includes('is_knowledge');
+  const htmlHasDataIsKnowledge = html.includes('data.is_knowledge');
+
+  console.log('📝 HTML内容检查:');
+  console.log('  包含 "知识内容":', htmlHas知识);
+  console.log('  包含 "is_knowledge":', htmlHasIsKnowledge);
+  console.log('  包含 "data.is_knowledge":', htmlHasDataIsKnowledge);
+  console.log('  HTML长度:', html.length);
+
+  // 如果bundle有但HTML没有,保存用于调试
+  if ((bundleHas知识 || bundleHasDataIsKnowledge) && !htmlHas知识 && !htmlHasDataIsKnowledge) {
+    console.log('  ⚠️  Bundle中有评估代码但HTML中没有!');
+    fs.writeFileSync(path.join(__dirname, 'debug_bundle.js'), bundleJs);
+    console.log('  已保存 debug_bundle.js 用于调试');
+  }
+
   // 写入输出文件
   fs.writeFileSync(outputFile, html);
 
-  // 清理临时文件
-  fs.unlinkSync(reactComponentPath);
-  fs.unlinkSync(path.join(__dirname, 'bundle_v2.js'));
+  // 调试:暂时保留bundle文件用于分析
+  console.log('📝 保留 bundle_v2.js 和 temp_flow_component_v2.jsx 用于调试');
+
+  // 清理临时文件(调试期间注释掉)
+  // fs.unlinkSync(reactComponentPath);
+  // fs.unlinkSync(path.join(__dirname, 'bundle_v2.js'));
 
   console.log('✅ Visualization generated: ' + outputFile);
   console.log('📊 Nodes: ' + Object.keys(data.nodes).length);

+ 7 - 0
visualization/knowledge_search_traverse/test_component.jsx

@@ -0,0 +1,7 @@
+import React from 'react';
+
+function TestComponent() {
+  return <div>知识内容测试</div>;
+}
+
+export default TestComponent;

+ 215 - 0
visualization/sug_v6_1_2_121/README.md

@@ -0,0 +1,215 @@
+# sug_v6_1_2_8 可视化工具
+
+## 概述
+
+这是 `sug_v6_1_2_8.py` 的配套可视化工具。**基于 v6.1.2.5 的 React Flow 可视化引擎**,通过数据转换层将 v6.1.2.8 的轮次数据转换为图结构,实现美观的交互式可视化。
+
+## 🎯 核心特性
+
+### 1. 智能数据转换
+- 自动检测数据格式(v6.1.2.5 或 v6.1.2.8)
+- 将轮次数据(rounds)转换为节点-边图结构
+- 保留完整的轮次信息和来源追踪
+
+### 2. 多类型节点支持
+| 节点类型 | 颜色 | 说明 |
+|---------|------|------|
+| `root` | 紫色 (#6b21a8) | 原始问题根节点 |
+| `seg` | 绿色 (#10b981) | 初始分词结果 |
+| `q` | 蓝色 (#3b82f6) | 查询节点 |
+| `search` | 深紫 (#8b5cf6) | 搜索操作节点 |
+| `note` | 粉色 (#ec4899) | 帖子结果节点 |
+
+### 3. 来源标识
+清楚展示每个Query的来源:
+- **seg** - 来自分词
+- **add** - 加词生成
+- **sug** - 建议词生成
+
+### 4. 交互功能
+继承 v6.1.2.5 的所有交互功能:
+- ✅ 节点拖拽和缩放
+- ✅ 按层级筛选
+- ✅ 节点搜索
+- ✅ 路径高亮
+- ✅ 目录树导航(左侧面板)
+- ✅ 节点折叠/展开
+- ✅ 聚焦功能
+- ✅ **目录卡片交错布局** - 卡片左右错开,避免垂直叠加
+- ✅ **目录卡片拖动** - 支持垂直方向拖动调整位置
+
+## 📁 文件结构
+
+```
+visualization/sug_v6_1_2_8/
+├── index.js                  # 主可视化脚本(支持格式检测)
+├── convert_v8_to_graph.js   # 数据转换层
+├── package.json             # 依赖配置
+├── node_modules/            # 依赖包(React Flow, esbuild等)
+└── README.md               # 本文档
+```
+
+## 🚀 使用方式
+
+### 方式1:通过主脚本自动生成
+
+```bash
+# 运行脚本并自动生成可视化
+python sug_v6_1_2_8.py --max-rounds 2 --sug-threshold 0.5 --visualize
+```
+
+### 方式2:手动生成可视化
+
+```bash
+# 从 run_context.json 生成可视化
+node visualization/sug_v6_1_2_8/index.js \
+  input/简单扣图/output/sug_v6_1_2_8/20251031/164016_6e/run_context.json \
+  output.html
+```
+
+## 📊 数据转换说明
+
+### 输入格式(v6.1.2.8)
+
+```json
+{
+  "o": "快速进行图片背景移除和替换",
+  "rounds": [
+    {
+      "round_num": 0,
+      "type": "initialization",
+      "seg_list": [
+        {"text": "快速", "score": 0.1},
+        {"text": "图片", "score": 0.1}
+      ],
+      "q_list_1": [...]
+    },
+    {
+      "round_num": 1,
+      "input_q_list": [...],
+      "output_q_list": [
+        {"text": "快速图片", "score": 0.2, "from": "add"}
+      ],
+      "search_count": 3
+    }
+  ]
+}
+```
+
+### 转换后格式(图结构)
+
+```json
+{
+  "nodes": {
+    "root_o": {
+      "type": "root",
+      "query": "快速进行图片背景移除和替换",
+      "level": 0
+    },
+    "seg_快速_0": {
+      "type": "seg",
+      "query": "快速",
+      "level": 1
+    },
+    "q_快速图片_r2_0": {
+      "type": "q",
+      "query": "快速图片",
+      "level": 2,
+      "from_source": "add"
+    }
+  },
+  "edges": [
+    {
+      "from": "root_o",
+      "to": "seg_快速_0",
+      "edge_type": "root_to_seg"
+    },
+    {
+      "from": "seg_快速_0",
+      "to": "q_快速_r1",
+      "edge_type": "seg_to_q"
+    }
+  ]
+}
+```
+
+## 🎨 可视化效果
+
+### 图布局
+- **水平布局**:从左到右按轮次展开
+- **层次化**:相同轮次的节点纵向排列
+- **自动布局**:使用 dagre 算法自动计算最佳位置
+
+### 节点样式
+- **边框颜色**:根据节点类型区分
+- **标签内容**:显示 Query 文本、分数、策略
+- **特殊标识**:
+  - 未选中的节点:红色 "未选中" 标签
+  - Search 节点:显示搜索次数和帖子数
+
+### 边样式
+- **虚线**:表示不同类型的关系
+- **颜色**:根据策略类型着色
+- **箭头**:指示数据流向
+
+## 📦 依赖
+
+所有依赖已包含在 `package.json` 中:
+- `react` + `react-dom` - UI 框架
+- `@xyflow/react` - 流程图库
+- `dagre` - 图布局算法
+- `esbuild` - 打包工具
+- `zustand` - 状态管理
+- `react-draggable` - 拖拽功能库
+
+**安装依赖:**
+```bash
+cd visualization/sug_v6_1_2_121
+npm install
+```
+
+总大小:约 34MB
+
+## 🔧 技术实现
+
+### 转换层(convert_v8_to_graph.js)
+- 解析 rounds 数据
+- 创建图节点和边
+- 维护轮次信息(iterations)
+
+### 可视化层(index.js)
+- 格式检测和自动转换
+- React Flow 渲染
+- 交互功能实现
+
+### 优势
+- ✅ 复用成熟的可视化引擎
+- ✅ 保持视觉一致性
+- ✅ 完整的交互功能
+- ✅ 易于维护和扩展
+
+## 📝 更新日志
+
+### v1.1.0 (2025-11-11)
+- ✨ 新增目录树卡片交错布局(之字形排列)
+- ✨ 新增目录树卡片拖动功能(支持垂直拖动)
+- 🔧 增加卡片间距从 8px 到 20px
+- 📦 添加 react-draggable 依赖
+
+### v1.0.0 (2025-10-31)
+- 基于 v6.1.2.5 可视化引擎
+- 实现轮次数据到图结构的转换
+- 支持新的节点类型(root, seg, q, search)
+- 自动格式检测和转换
+- 完整的交互功能
+
+## 🤝 兼容性
+
+| 版本 | 支持 | 说明 |
+|------|------|------|
+| v6.1.2.5 | ✅ | 直接渲染 query_graph.json |
+| v6.1.2.8 | ✅ | 自动转换 run_context.json |
+
+## 📧 问题反馈
+
+如有问题或建议,请查看主项目 README 或提交 Issue。

+ 76 - 4
visualization/sug_v6_1_2_121/convert_v8_to_graph_v3.js

@@ -6,10 +6,11 @@
 // 得分提升阈值:与Python代码保持一致
 const REQUIRED_SCORE_GAIN = 0.02;
 
-function convertV8ToGraphV2(runContext, searchResults) {
+function convertV8ToGraphV2(runContext, searchResults, extractionData) {
   const nodes = {};
   const edges = [];
   const iterations = {};
+  extractionData = extractionData || {}; // 默认为空对象
 
   const o = runContext.o || '原始问题';
   const rounds = runContext.rounds || [];
@@ -455,7 +456,9 @@ function convertV8ToGraphV2(runContext, searchResults) {
                   body_text: post.body_text || '',
                   images: post.images || [],
                   image_list: imageList,
-                  interact_info: post.interact_info || {}
+                  interact_info: post.interact_info || {},
+                  // 附加多模态提取数据
+                  extraction: extractionData && extractionData[post.note_id] ? extractionData[post.note_id] : null
                 };
 
                 edges.push({
@@ -465,6 +468,38 @@ function convertV8ToGraphV2(runContext, searchResults) {
                   strategy: '搜索结果'
                 });
 
+                // 如果有提取数据,创建对应的分析节点
+                if (extractionData && extractionData[post.note_id]) {
+                  const analysisId = `analysis_${post.note_id}_${searchIndex}_${postIndex}`;
+
+                  nodes[analysisId] = {
+                    type: 'analysis',
+                    query: '[AI分析] ' + post.title,
+                    level: roundNum * 10 + 4,
+                    relevance_score: 0,
+                    strategy: 'AI分析',
+                    iteration: roundNum,
+                    is_selected: true,
+                    note_id: post.note_id,
+                    note_url: post.note_url,
+                    title: post.title,
+                    body_text: post.body_text || '',
+                    interact_info: post.interact_info || {},
+                    extraction: extractionData[post.note_id],
+                    image_list: imageList
+                  };
+
+                  edges.push({
+                    from: postId,
+                    to: analysisId,
+                    edge_type: 'post_to_analysis',
+                    strategy: 'AI分析'
+                  });
+
+                  if (!iterations[roundNum * 10 + 4]) iterations[roundNum * 10 + 4] = [];
+                  iterations[roundNum * 10 + 4].push(analysisId);
+                }
+
                 if (!iterations[roundNum * 10 + 3]) iterations[roundNum * 10 + 3] = [];
                 iterations[roundNum * 10 + 3].push(postId);
               });
@@ -729,10 +764,11 @@ function convertV8ToGraphV2(runContext, searchResults) {
  * - 步骤信息放在边上
  * - 隐藏Round/Step节点
  */
-function convertV8ToGraphSimplified(runContext, searchResults) {
+function convertV8ToGraphSimplified(runContext, searchResults, extractionData) {
   const mergedNodes = {};
   const edges = [];
   const iterations = {};
+  extractionData = extractionData || {}; // 默认为空对象
 
   const o = runContext.o || '原始问题';
   const rounds = runContext.rounds || [];
@@ -931,11 +967,47 @@ function convertV8ToGraphSimplified(runContext, searchResults) {
       image_list: imageList,
       interact_info: post.interact_info || {},
       foundByQueries: Array.from(post.foundByQueries),
-      foundInRounds: Array.from(post.foundInRounds)
+      foundInRounds: Array.from(post.foundInRounds),
+      // 附加多模态提取数据
+      extraction: extractionData && extractionData[post.note_id] ? extractionData[post.note_id] : null
     };
 
     if (!iterations[100]) iterations[100] = [];
     iterations[100].push(postId);
+
+    // 如果有提取数据,创建对应的分析节点
+    if (extractionData && extractionData[post.note_id]) {
+      const analysisId = `analysis_${noteId}`;
+
+      mergedNodes[analysisId] = {
+        type: 'analysis',
+        query: '[AI分析] ' + post.title,
+        level: 101,
+        relevance_score: 0,
+        strategy: 'AI分析',
+        iteration: Math.max(...Array.from(post.foundInRounds)),
+        is_selected: true,
+        note_id: post.note_id,
+        note_url: post.note_url,
+        title: post.title,
+        body_text: post.body_text || '',
+        interact_info: post.interact_info || {},
+        extraction: extractionData[post.note_id],
+        image_list: imageList
+      };
+
+      edges.push({
+        from: postId,
+        to: analysisId,
+        edge_type: 'post_to_analysis',
+        strategy: 'AI分析',
+        label: 'AI分析',
+        round: Math.max(...Array.from(post.foundInRounds))
+      });
+
+      if (!iterations[101]) iterations[101] = [];
+      iterations[101].push(analysisId);
+    }
   });
 
   // 第三遍:创建边

+ 215 - 3
visualization/sug_v6_1_2_121/index.js

@@ -36,6 +36,16 @@ if (inputData.rounds && inputData.o) {
     console.log('✅ 使用 run_context.json 中的内嵌搜索结果');
   }
 
+  // 尝试读取 search_extract.json(多模态提取数据)
+  let extractionData = null;
+  const extractionPath = path.join(path.dirname(inputFile), 'search_extract.json');
+  if (fs.existsSync(extractionPath)) {
+    console.log('📸 读取多模态提取数据...');
+    extractionData = JSON.parse(fs.readFileSync(extractionPath, 'utf-8'));
+  } else {
+    console.log('ℹ️  未找到 search_extract.json,跳过多模态展示');
+  }
+
   // 选择转换函数
   let graphData;
   let fullData = null; // 用于目录的完整数据
@@ -43,9 +53,9 @@ if (inputData.rounds && inputData.o) {
   if (useSimplified) {
     console.log('🎨 使用简化视图(合并query节点)');
     // 生成简化版用于画布
-    graphData = convertV8ToGraphSimplified(inputData, searchResults);
+    graphData = convertV8ToGraphSimplified(inputData, searchResults, extractionData);
     // 生成完整版用于目录
-    const fullGraphData = convertV8ToGraphV2(inputData, searchResults);
+    const fullGraphData = convertV8ToGraphV2(inputData, searchResults, extractionData);
     fullData = {
       nodes: fullGraphData.nodes,
       edges: fullGraphData.edges,
@@ -55,7 +65,7 @@ if (inputData.rounds && inputData.o) {
     console.log(`📋 完整版(用于目录): ${Object.keys(fullData.nodes).length} 个节点`);
   } else {
     console.log('📊 使用详细视图(完整流程)');
-    graphData = convertV8ToGraphV2(inputData, searchResults);
+    graphData = convertV8ToGraphV2(inputData, searchResults, extractionData);
     console.log(`✅ 转换完成: ${Object.keys(graphData.nodes).length} 个节点, ${graphData.edges.length} 条边`);
   }
 
@@ -756,9 +766,211 @@ function NoteNode({ id, data, sourcePosition, targetPosition }) {
   );
 }
 
+// AnalysisNode 组件:展示AI分析(左侧OCR文字,右侧缩略图+描述)
+function AnalysisNode({ data }) {
+  const nodeStyle = {
+    background: '#fffbeb',
+    border: '2px solid #fbbf24',
+    borderRadius: '8px',
+    padding: '12px',
+    minWidth: '600px',
+    maxWidth: '800px',
+    fontSize: '12px',
+    boxShadow: '0 4px 6px rgba(0,0,0,0.1)',
+  };
+
+  return (
+    <div style={nodeStyle}>
+      <Handle
+        type="target"
+        position={Position.Left}
+        style={{ background: '#fbbf24', width: 8, height: 8 }}
+      />
+
+      {/* 标题 */}
+      <div style={{
+        fontSize: '14px',
+        fontWeight: 'bold',
+        marginBottom: '8px',
+        color: '#92400e',
+      }}>
+        🖼️ {data.query}
+      </div>
+
+      {/* 评分和互动数据 */}
+      <div style={{
+        display: 'flex',
+        justifyContent: 'space-between',
+        marginBottom: '8px',
+        padding: '6px',
+        background: '#fef3c7',
+        borderRadius: '4px',
+      }}>
+        <div style={{ fontSize: '11px', fontWeight: 'bold' }}>
+          Score: {data.interact_info?.relevance_score || 0}
+        </div>
+        <div style={{ display: 'flex', gap: '12px', fontSize: '11px' }}>
+          {data.interact_info?.liked_count > 0 && (
+            <span>❤️ {data.interact_info.liked_count}</span>
+          )}
+          {data.interact_info?.collected_count > 0 && (
+            <span>⭐ {data.interact_info.collected_count}</span>
+          )}
+          {data.interact_info?.comment_count > 0 && (
+            <span>💬 {data.interact_info.comment_count}</span>
+          )}
+        </div>
+      </div>
+
+      {/* 完整正文内容 */}
+      {data.body_text && (
+        <div style={{
+          padding: '8px',
+          background: 'white',
+          borderRadius: '4px',
+          marginBottom: '12px',
+          fontSize: '11px',
+          lineHeight: '1.5',
+          maxHeight: '120px',
+          overflow: 'auto',
+          border: '1px solid #fbbf24',
+        }}>
+          {data.body_text}
+        </div>
+      )}
+
+      {/* AI分析 - 左右分栏 */}
+      {data.extraction && data.extraction.images && (
+        <div>
+          {data.extraction.images.map((img, idx) => (
+            <div
+              key={idx}
+              style={{
+                display: 'grid',
+                gridTemplateColumns: '2fr 1fr',
+                gap: '16px',
+                marginBottom: '12px',
+                padding: '10px',
+                background: 'white',
+                borderRadius: '4px',
+                border: '1px solid #d97706',
+              }}
+            >
+              {/* 左侧:OCR提取文字 */}
+              <div>
+                <div style={{
+                  fontSize: '11px',
+                  fontWeight: 'bold',
+                  color: '#92400e',
+                  marginBottom: '6px',
+                }}>
+                  📝 图片 {idx + 1}/{data.extraction.images.length}
+                </div>
+
+                {img.extract_text && (
+                  <div style={{
+                    fontSize: '11px',
+                    color: '#1f2937',
+                    lineHeight: '1.6',
+                    padding: '8px',
+                    background: '#fef9e7',
+                    borderRadius: '3px',
+                    borderLeft: '3px solid #f39c12',
+                  }}>
+                    <div style={{
+                      fontSize: '10px',
+                      fontWeight: 'bold',
+                      color: '#d97706',
+                      marginBottom: '4px',
+                    }}>
+                      【提取文字】
+                    </div>
+                    {img.extract_text}
+                  </div>
+                )}
+              </div>
+
+              {/* 右侧:缩略图 + 描述 */}
+              <div style={{
+                display: 'flex',
+                flexDirection: 'column',
+                gap: '8px',
+              }}>
+                {/* 缩略图 */}
+                {data.image_list && data.image_list[idx] && (
+                  <img
+                    src={(data.image_list[idx].image_url || data.image_list[idx])}
+                    alt={'图片' + (idx + 1)}
+                    style={{
+                      width: '100%',
+                      maxWidth: '150px',
+                      height: 'auto',
+                      maxHeight: '150px',
+                      objectFit: 'cover',
+                      borderRadius: '4px',
+                      border: '1px solid #d97706',
+                      cursor: 'pointer',
+                    }}
+                    onError={(e) => {
+                      e.target.style.display = 'none';
+                    }}
+                  />
+                )}
+
+                {/* 描述文字(带悬浮显示全部) */}
+                {img.description && (
+                  <div
+                    style={{
+                      fontSize: '9px',
+                      color: '#78350f',
+                      lineHeight: '1.3',
+                      maxHeight: '60px',
+                      overflow: 'hidden',
+                      textOverflow: 'ellipsis',
+                      position: 'relative',
+                      cursor: 'help',
+                    }}
+                    title={img.description}
+                  >
+                    {img.description.length > 80
+                      ? img.description.substring(0, 80) + '...'
+                      : img.description
+                    }
+                  </div>
+                )}
+              </div>
+            </div>
+          ))}
+        </div>
+      )}
+
+      {/* 查看原帖链接 */}
+      {data.note_url && (
+        <div style={{ marginTop: '8px', fontSize: '10px' }}>
+          <a
+            href={data.note_url}
+            target="_blank"
+            rel="noopener noreferrer"
+            style={{ color: '#92400e', textDecoration: 'underline' }}
+          >
+            🔗 查看原帖
+          </a>
+        </div>
+      )}
+
+      <Handle
+        type="source"
+        position={Position.Right}
+        style={{ background: '#fbbf24', width: 8, height: 8 }}
+      />
+    </div>
+  );
+}
+
 const nodeTypes = {
   query: QueryNode,
   note: NoteNode,
+  analysis: AnalysisNode,
 };
 
 // 根据 score 获取颜色

+ 4 - 3
visualization/sug_v6_1_2_5/package.json → visualization/sug_v6_1_2_121/package.json

@@ -1,7 +1,7 @@
 {
   "name": "sug-v6-1-2-5-visualization",
-  "version": "1.0.0",
-  "description": "可视化工具 for sug_v6_1_2_5.py - React Flow based query graph visualization",
+  "version": "1.1.0",
+  "description": "可视化工具 for sug_v6_1_2_5.py - React Flow based query graph visualization with draggable directory tree",
   "main": "index.js",
   "scripts": {
     "visualize": "node index.js"
@@ -12,7 +12,8 @@
     "esbuild": "^0.25.11",
     "@xyflow/react": "^12.9.1",
     "dagre": "^0.8.5",
-    "zustand": "^5.0.2"
+    "zustand": "^5.0.2",
+    "react-draggable": "^4.4.6"
   },
   "keywords": [
     "visualization",

+ 0 - 107
visualization/sug_v6_1_2_5/README.md

@@ -1,107 +0,0 @@
-# sug_v6_1_2_5 可视化工具
-
-这是 `sug_v6_1_2_5.py` 的配套可视化工具,用于将 `query_graph.json` 转换为交互式 HTML 可视化页面。
-
-## 📁 文件结构
-
-```
-visualization/
-└── sug_v6_1_2_5/           # 与主脚本名称对应
-    ├── index.js            # 主可视化脚本(统一命名)
-    ├── package.json        # 依赖配置文件
-    ├── node_modules/       # 所有依赖包(通过 npm install 安装)
-    │   ├── esbuild/
-    │   ├── @xyflow/
-    │   ├── react/
-    │   ├── react-dom/
-    │   ├── dagre/
-    │   └── ...
-    └── README.md           # 本文件
-```
-
-## 📦 安装依赖
-
-首次使用或克隆代码后,需要安装依赖包:
-
-```bash
-cd visualization/sug_v6_1_2_5
-npm install
-```
-
-这将安装所有必需的依赖包(约 33MB)。
-
-## ✨ 特性
-
-- **独立运行**:所有依赖都包含在文件夹内,无需外部 node_modules
-- **React Flow 可视化**:基于 @xyflow/react 的现代化图形展示
-- **交互功能**:
-  - 节点拖拽
-  - 层级筛选
-  - 节点搜索
-  - 路径高亮
-  - 目录树导航
-  - 面包屑路径
-  - 值得搜索的查询列表
-- **节点类型区分**:
-  - 🔍 查询节点(query)
-  - 📝 帖子节点(note)
-- **状态标识**:
-  - 未选中查询:红色文字
-  - 不满足的帖子(match_level: unsatisfied):红色文字
-
-## 🚀 使用方式
-
-### 方式1:通过 sug_v6_1_2_5.py 调用
-
-```bash
-# 运行主程序并自动生成可视化
-python sug_v6_1_2_5.py --input-dir "input/某目录" --visualize
-
-# 仅生成可视化(不运行主程序)
-python sug_v6_1_2_5.py --visualize-only "path/to/query_graph.json"
-```
-
-### 方式2:直接调用脚本
-
-```bash
-node visualization/sug_v6_1_2_5/index.js <query_graph.json> [output.html]
-```
-
-## 📊 输入/输出
-
-**输入**:`query_graph.json` - 包含节点和边信息的 JSON 文件
-```json
-{
-  "nodes": {
-    "node_id": {
-      "type": "query" | "note",
-      "query": "...",
-      "level": 1,
-      "relevance_score": 0.95,
-      ...
-    }
-  },
-  "edges": [
-    {
-      "from": "node_id_1",
-      "to": "node_id_2",
-      "edge_type": "..."
-    }
-  ]
-}
-```
-
-**输出**:`visualization.html` - 独立的 HTML 文件,包含所有 JavaScript 和 CSS
-
-## 📦 依赖大小
-
-- 总大小:约 33MB
-- 所有依赖已打包,无需额外安装
-
-## 🔧 技术栈
-
-- Node.js 运行环境
-- esbuild(打包工具)
-- React + React DOM
-- @xyflow/react(流程图库)
-- dagre(图布局算法)

+ 0 - 1966
visualization/sug_v6_1_2_5/index.js

@@ -1,1966 +0,0 @@
-#!/usr/bin/env node
-
-const fs = require('fs');
-const path = require('path');
-const { build } = require('esbuild');
-
-// 读取命令行参数
-const args = process.argv.slice(2);
-if (args.length === 0) {
-  console.error('Usage: node index.js <path-to-query_graph.json> [output.html]');
-  process.exit(1);
-}
-
-const inputFile = args[0];
-const outputFile = args[1] || 'query_graph_output.html';
-
-// 读取输入数据
-const data = JSON.parse(fs.readFileSync(inputFile, 'utf-8'));
-
-// 创建临时 React 组件文件
-const reactComponentPath = path.join(__dirname, 'temp_flow_component_v2.jsx');
-const reactComponent = `
-import React, { useState, useCallback, useMemo, useEffect } from 'react';
-import { createRoot } from 'react-dom/client';
-import {
-  ReactFlow,
-  Controls,
-  Background,
-  useNodesState,
-  useEdgesState,
-  Handle,
-  Position,
-  useReactFlow,
-  ReactFlowProvider,
-} from '@xyflow/react';
-import '@xyflow/react/dist/style.css';
-
-const data = ${JSON.stringify(data, null, 2)};
-
-// 查询节点组件 - 卡片样式
-function QueryNode({ id, data, sourcePosition, targetPosition }) {
-  // 所有节点默认展开
-  const expanded = true;
-
-  return (
-    <div>
-      <Handle
-        type="target"
-        position={targetPosition || Position.Left}
-        style={{ background: '#667eea', width: 8, height: 8 }}
-      />
-      <div
-        style={{
-          padding: '12px',
-          borderRadius: '8px',
-          border: data.isHighlighted ? '3px solid #667eea' :
-                  data.isCollapsed ? '2px solid #667eea' :
-                  data.isSelected === false ? '2px dashed #d1d5db' :
-                  data.level === 0 ? '2px solid #8b5cf6' : '1px solid #e5e7eb',
-          background: data.isHighlighted ? '#eef2ff' :
-                      data.isSelected === false ? '#f9fafb' : 'white',
-          minWidth: '200px',
-          maxWidth: '280px',
-          boxShadow: data.isHighlighted ? '0 0 0 4px rgba(102, 126, 234, 0.25), 0 4px 16px rgba(102, 126, 234, 0.4)' :
-                     data.isCollapsed ? '0 4px 12px rgba(102, 126, 234, 0.15)' :
-                     data.level === 0 ? '0 4px 12px rgba(139, 92, 246, 0.15)' : '0 2px 6px rgba(0, 0, 0, 0.06)',
-          transition: 'all 0.3s ease',
-          cursor: 'pointer',
-          position: 'relative',
-          opacity: data.isSelected === false ? 0.6 : 1,
-        }}
-      >
-        {/* 折叠当前节点按钮 - 左边 */}
-        <div
-          style={{
-            position: 'absolute',
-            top: '6px',
-            left: '6px',
-            width: '20px',
-            height: '20px',
-            borderRadius: '50%',
-            background: '#f59e0b',
-            color: 'white',
-            display: 'flex',
-            alignItems: 'center',
-            justifyContent: 'center',
-            fontSize: '11px',
-            fontWeight: 'bold',
-            cursor: 'pointer',
-            transition: 'all 0.2s ease',
-            zIndex: 10,
-          }}
-          onClick={(e) => {
-            e.stopPropagation();
-            if (data.onHideSelf) {
-              data.onHideSelf();
-            }
-          }}
-          onMouseEnter={(e) => {
-            e.currentTarget.style.background = '#d97706';
-          }}
-          onMouseLeave={(e) => {
-            e.currentTarget.style.background = '#f59e0b';
-          }}
-          title="隐藏当前节点"
-        >
-          ×
-        </div>
-
-        {/* 聚焦按钮 - 右上角 */}
-        <div
-          style={{
-            position: 'absolute',
-            top: '6px',
-            right: '6px',
-            width: '20px',
-            height: '20px',
-            borderRadius: '50%',
-            background: data.isFocused ? '#10b981' : '#e5e7eb',
-            color: data.isFocused ? 'white' : '#6b7280',
-            display: 'flex',
-            alignItems: 'center',
-            justifyContent: 'center',
-            fontSize: '11px',
-            fontWeight: 'bold',
-            cursor: 'pointer',
-            transition: 'all 0.2s ease',
-            zIndex: 10,
-          }}
-          onClick={(e) => {
-            e.stopPropagation();
-            if (data.onFocus) {
-              data.onFocus();
-            }
-          }}
-          onMouseEnter={(e) => {
-            if (!data.isFocused) {
-              e.currentTarget.style.background = '#d1d5db';
-            }
-          }}
-          onMouseLeave={(e) => {
-            if (!data.isFocused) {
-              e.currentTarget.style.background = '#e5e7eb';
-            }
-          }}
-          title={data.isFocused ? '取消聚焦' : '聚焦到此节点'}
-        >
-          🎯
-        </div>
-
-        {/* 折叠/展开子节点按钮 - 右边第二个位置 */}
-        {data.hasChildren && (
-          <div
-            style={{
-              position: 'absolute',
-              top: '6px',
-              right: '30px',
-              width: '20px',
-              height: '20px',
-              borderRadius: '50%',
-              background: data.isCollapsed ? '#667eea' : '#e5e7eb',
-              color: data.isCollapsed ? 'white' : '#6b7280',
-              display: 'flex',
-              alignItems: 'center',
-              justifyContent: 'center',
-              fontSize: '11px',
-              fontWeight: 'bold',
-              cursor: 'pointer',
-              transition: 'all 0.2s ease',
-              zIndex: 10,
-            }}
-            onClick={(e) => {
-              e.stopPropagation();
-              data.onToggleCollapse();
-            }}
-            title={data.isCollapsed ? '展开子节点' : '折叠子节点'}
-          >
-            {data.isCollapsed ? '+' : '−'}
-          </div>
-        )}
-
-        {/* 卡片内容 */}
-        <div>
-          {/* 标题行 */}
-          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: '8px', paddingLeft: '24px', paddingRight: data.hasChildren ? '54px' : '28px' }}>
-            <div style={{ flex: 1 }}>
-              <div style={{ display: 'flex', alignItems: 'center', gap: '4px', marginBottom: '3px' }}>
-                <div style={{
-                  fontSize: '13px',
-                  fontWeight: data.level === 0 ? '700' : '600',
-                  color: data.level === 0 ? '#6b21a8' : '#1f2937',
-                  lineHeight: '1.3',
-                  flex: 1,
-                }}>
-                  {data.title}
-                </div>
-                {data.isSelected === false && (
-                  <div style={{
-                    fontSize: '9px',
-                    padding: '1px 4px',
-                    borderRadius: '3px',
-                    background: '#fee2e2',
-                    color: '#991b1b',
-                    fontWeight: '500',
-                    flexShrink: 0,
-                  }}>
-                    未选中
-                  </div>
-                )}
-              </div>
-            </div>
-          </div>
-
-        {/* 展开的详细信息 - 始终显示 */}
-        <div style={{ fontSize: '11px', lineHeight: 1.4 }}>
-            <div style={{ display: 'flex', gap: '4px', marginBottom: '6px', flexWrap: 'wrap' }}>
-              <span style={{
-                display: 'inline-block',
-                padding: '1px 6px',
-                borderRadius: '10px',
-                background: '#eff6ff',
-                color: '#3b82f6',
-                fontSize: '10px',
-                fontWeight: '500',
-              }}>
-                Lv.{data.level}
-              </span>
-              <span style={{
-                display: 'inline-block',
-                padding: '1px 6px',
-                borderRadius: '10px',
-                background: '#f0fdf4',
-                color: '#16a34a',
-                fontSize: '10px',
-                fontWeight: '500',
-              }}>
-                {data.score}
-              </span>
-              {data.strategy && data.strategy !== 'root' && (
-                <span style={{
-                  display: 'inline-block',
-                  padding: '1px 6px',
-                  borderRadius: '10px',
-                  background: '#fef3c7',
-                  color: '#92400e',
-                  fontSize: '10px',
-                  fontWeight: '500',
-                }}>
-                  {data.strategy}
-                </span>
-              )}
-            </div>
-
-            {data.parent && (
-              <div style={{ color: '#6b7280', fontSize: '10px', marginTop: '4px', paddingTop: '4px', borderTop: '1px solid #f3f4f6' }}>
-                <strong>Parent:</strong> {data.parent}
-              </div>
-            )}
-            {data.evaluationReason && (
-              <div style={{
-                marginTop: '6px',
-                paddingTop: '6px',
-                borderTop: '1px solid #f3f4f6',
-                fontSize: '10px',
-                color: '#6b7280',
-                lineHeight: '1.5',
-              }}>
-                <strong style={{ color: '#4b5563' }}>评估:</strong>
-                <div style={{ marginTop: '2px' }}>{data.evaluationReason}</div>
-              </div>
-            )}
-          </div>
-        </div>
-      </div>
-      <Handle
-        type="source"
-        position={sourcePosition || Position.Right}
-        style={{ background: '#667eea', width: 8, height: 8 }}
-      />
-    </div>
-  );
-}
-
-// 笔记节点组件 - 卡片样式,带轮播图
-function NoteNode({ id, data, sourcePosition, targetPosition }) {
-  const [currentImageIndex, setCurrentImageIndex] = useState(0);
-  const expanded = true;
-  const hasImages = data.imageList && data.imageList.length > 0;
-
-  const nextImage = (e) => {
-    e.stopPropagation();
-    if (hasImages) {
-      setCurrentImageIndex((prev) => (prev + 1) % data.imageList.length);
-    }
-  };
-
-  const prevImage = (e) => {
-    e.stopPropagation();
-    if (hasImages) {
-      setCurrentImageIndex((prev) => (prev - 1 + data.imageList.length) % data.imageList.length);
-    }
-  };
-
-  return (
-    <div>
-      <Handle
-        type="target"
-        position={targetPosition || Position.Left}
-        style={{ background: '#ec4899', width: 8, height: 8 }}
-      />
-      <div
-        style={{
-          padding: '14px',
-          borderRadius: '20px',
-          border: data.isHighlighted ? '3px solid #ec4899' : '2px solid #fce7f3',
-          background: data.isHighlighted ? '#eef2ff' : 'white',
-          minWidth: '220px',
-          maxWidth: '300px',
-          boxShadow: data.isHighlighted ? '0 0 0 4px rgba(236, 72, 153, 0.25), 0 4px 16px rgba(236, 72, 153, 0.4)' : '0 4px 12px rgba(236, 72, 153, 0.15)',
-          transition: 'all 0.3s ease',
-          cursor: 'pointer',
-        }}
-      >
-        {/* 笔记图标和标题 */}
-        <div style={{ display: 'flex', alignItems: 'flex-start', marginBottom: '8px' }}>
-          <span style={{ fontSize: '16px', marginRight: '8px' }}>📝</span>
-          <div style={{ flex: 1 }}>
-            <div style={{
-              fontSize: '13px',
-              fontWeight: '600',
-              color: '#831843',
-              lineHeight: '1.4',
-              marginBottom: '4px',
-            }}>
-              {data.title}
-            </div>
-          </div>
-        </div>
-
-        {/* 轮播图 */}
-        {hasImages && (
-          <div style={{
-            position: 'relative',
-            marginBottom: '8px',
-            borderRadius: '12px',
-            overflow: 'hidden',
-          }}>
-            <img
-              src={data.imageList[currentImageIndex].image_url}
-              alt={\`Image \${currentImageIndex + 1}\`}
-              style={{
-                width: '100%',
-                height: '160px',
-                objectFit: 'cover',
-                display: 'block',
-              }}
-              onError={(e) => {
-                e.target.style.display = 'none';
-              }}
-            />
-            {data.imageList.length > 1 && (
-              <>
-                {/* 左右切换按钮 */}
-                <button
-                  onClick={prevImage}
-                  style={{
-                    position: 'absolute',
-                    left: '4px',
-                    top: '50%',
-                    transform: 'translateY(-50%)',
-                    background: 'rgba(0, 0, 0, 0.5)',
-                    color: 'white',
-                    border: 'none',
-                    borderRadius: '50%',
-                    width: '24px',
-                    height: '24px',
-                    cursor: 'pointer',
-                    display: 'flex',
-                    alignItems: 'center',
-                    justifyContent: 'center',
-                    fontSize: '14px',
-                  }}
-                >
-                  ‹
-                </button>
-                <button
-                  onClick={nextImage}
-                  style={{
-                    position: 'absolute',
-                    right: '4px',
-                    top: '50%',
-                    transform: 'translateY(-50%)',
-                    background: 'rgba(0, 0, 0, 0.5)',
-                    color: 'white',
-                    border: 'none',
-                    borderRadius: '50%',
-                    width: '24px',
-                    height: '24px',
-                    cursor: 'pointer',
-                    display: 'flex',
-                    alignItems: 'center',
-                    justifyContent: 'center',
-                    fontSize: '14px',
-                  }}
-                >
-                  ›
-                </button>
-                {/* 图片计数 */}
-                <div style={{
-                  position: 'absolute',
-                  bottom: '4px',
-                  right: '4px',
-                  background: 'rgba(0, 0, 0, 0.6)',
-                  color: 'white',
-                  padding: '2px 6px',
-                  borderRadius: '10px',
-                  fontSize: '10px',
-                }}>
-                  {currentImageIndex + 1}/{data.imageList.length}
-                </div>
-              </>
-            )}
-          </div>
-        )}
-
-        {/* 标签 */}
-        <div style={{ display: 'flex', gap: '6px', marginBottom: '8px', flexWrap: 'wrap' }}>
-          <span style={{
-            display: 'inline-block',
-            padding: '2px 8px',
-            borderRadius: '12px',
-            background: '#fff1f2',
-            color: '#be123c',
-            fontSize: '10px',
-            fontWeight: '500',
-          }}>
-            {data.matchLevel}
-          </span>
-          <span style={{
-            display: 'inline-block',
-            padding: '2px 8px',
-            borderRadius: '12px',
-            background: '#fff7ed',
-            color: '#c2410c',
-            fontSize: '10px',
-            fontWeight: '500',
-          }}>
-            Score: {data.score}
-          </span>
-        </div>
-
-        {/* 描述 */}
-        {expanded && data.description && (
-          <div style={{
-            fontSize: '11px',
-            color: '#9f1239',
-            lineHeight: '1.5',
-            paddingTop: '8px',
-            borderTop: '1px solid #fbcfe8',
-          }}>
-            {data.description}
-          </div>
-        )}
-
-        {/* 评估理由 */}
-        {expanded && data.evaluationReason && (
-          <div style={{
-            fontSize: '10px',
-            color: '#831843',
-            lineHeight: '1.5',
-            paddingTop: '8px',
-            marginTop: '8px',
-            borderTop: '1px solid #fbcfe8',
-          }}>
-            <strong style={{ color: '#9f1239' }}>评估:</strong>
-            <div style={{ marginTop: '2px' }}>{data.evaluationReason}</div>
-          </div>
-        )}
-      </div>
-      <Handle
-        type="source"
-        position={sourcePosition || Position.Right}
-        style={{ background: '#ec4899', width: 8, height: 8 }}
-      />
-    </div>
-  );
-}
-
-const nodeTypes = {
-  query: QueryNode,
-  note: NoteNode,
-};
-
-// 根据 score 获取颜色
-function getScoreColor(score) {
-  if (score >= 0.7) return '#10b981'; // 绿色 - 高分
-  if (score >= 0.4) return '#f59e0b'; // 橙色 - 中分
-  return '#ef4444'; // 红色 - 低分
-}
-
-// 截断文本,保留头尾,中间显示省略号
-function truncateMiddle(text, maxLength = 20) {
-  if (!text || text.length <= maxLength) return text;
-  const headLength = Math.ceil(maxLength * 0.4);
-  const tailLength = Math.floor(maxLength * 0.4);
-  const head = text.substring(0, headLength);
-  const tail = text.substring(text.length - tailLength);
-  return \`\${head}...\${tail}\`;
-}
-
-// 根据策略获取颜色
-function getStrategyColor(strategy) {
-  const strategyColors = {
-    '初始分词': '#10b981',
-    '调用sug': '#06b6d4',
-    '同义改写': '#f59e0b',
-    '加词': '#3b82f6',
-    '抽象改写': '#8b5cf6',
-    '基于部分匹配改进': '#ec4899',
-    '结果分支-抽象改写': '#a855f7',
-    '结果分支-同义改写': '#fb923c',
-  };
-  return strategyColors[strategy] || '#9ca3af';
-}
-
-// 树节点组件
-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 strategyColor = getStrategyColor(strategy);
-
-  return (
-    <div style={{ marginLeft: level * 12 + 'px' }}>
-      <div
-        style={{
-          padding: '6px 8px',
-          borderRadius: '4px',
-          cursor: 'pointer',
-          background: 'transparent',
-          border: isSelected ? '1px solid #3b82f6' : '1px solid transparent',
-          display: 'flex',
-          alignItems: 'center',
-          gap: '6px',
-          transition: 'all 0.2s ease',
-          position: 'relative',
-          overflow: 'visible',
-        }}
-        onMouseEnter={(e) => {
-          if (!isSelected) e.currentTarget.style.background = '#f9fafb';
-        }}
-        onMouseLeave={(e) => {
-          if (!isSelected) e.currentTarget.style.background = 'transparent';
-        }}
-      >
-        {/* 策略类型竖线 */}
-        <div style={{
-          width: '3px',
-          height: '20px',
-          background: strategyColor,
-          borderRadius: '2px',
-          flexShrink: 0,
-          position: 'relative',
-          zIndex: 1,
-        }} />
-
-        {hasChildren && (
-          <span
-            style={{
-              fontSize: '10px',
-              color: '#6b7280',
-              cursor: 'pointer',
-              width: '16px',
-              textAlign: 'center',
-              position: 'relative',
-              zIndex: 1,
-            }}
-            onClick={(e) => {
-              e.stopPropagation();
-              onToggle();
-            }}
-          >
-            {isCollapsed ? '▶' : '▼'}
-          </span>
-        )}
-        {!hasChildren && <span style={{ width: '16px', position: 'relative', zIndex: 1 }}></span>}
-
-        <div
-          style={{
-            flex: 1,
-            fontSize: '12px',
-            color: '#374151',
-            position: 'relative',
-            zIndex: 1,
-            minWidth: 0,
-            display: 'flex',
-            flexDirection: 'column',
-            gap: '4px',
-          }}
-          onClick={onSelect}
-        >
-          <div style={{
-            display: 'flex',
-            alignItems: 'center',
-            gap: '8px',
-          }}>
-            {/* 节点类型图标 */}
-            <span style={{
-              fontSize: '12px',
-              flexShrink: 0,
-            }}>
-              {node.type === 'note' ? '📝' : '🔍'}
-            </span>
-
-            <div style={{
-              fontWeight: level === 0 ? '600' : '400',
-              maxWidth: '180px',
-              flex: 1,
-              minWidth: 0,
-              color: (node.type === 'note' ? node.data.matchLevel === 'unsatisfied' : node.data.isSelected === false) ? '#ef4444' : '#374151',
-            }}
-            title={node.data.title || node.id}
-            >
-              {truncateMiddle(node.data.title || node.id, 18)}
-            </div>
-
-            {/* 分数显示 */}
-            <span style={{
-              fontSize: '11px',
-              color: '#6b7280',
-              fontWeight: '500',
-              flexShrink: 0,
-            }}>
-              {score.toFixed(2)}
-            </span>
-          </div>
-
-          {/* 分数下划线 */}
-          <div style={{
-            width: (score * 100) + '%',
-            height: '2px',
-            background: getScoreColor(score),
-            borderRadius: '1px',
-          }} />
-        </div>
-      </div>
-
-      {hasChildren && !isCollapsed && (
-        <div>
-          {children}
-        </div>
-      )}
-    </div>
-  );
-}
-
-// 使用 dagre 自动布局
-function getLayoutedElements(nodes, edges, direction = 'LR') {
-  console.log('🎯 Starting layout with dagre...');
-  console.log('Input:', nodes.length, 'nodes,', edges.length, 'edges');
-
-  // 检查 dagre 是否加载
-  if (typeof window === 'undefined' || typeof window.dagre === 'undefined') {
-    console.warn('⚠️ Dagre not loaded, using fallback layout');
-    // 降级到简单布局
-    const levelGroups = {};
-    nodes.forEach(node => {
-      const level = node.data.level || 0;
-      if (!levelGroups[level]) levelGroups[level] = [];
-      levelGroups[level].push(node);
-    });
-
-    Object.entries(levelGroups).forEach(([level, nodeList]) => {
-      const x = parseInt(level) * 350;
-      nodeList.forEach((node, index) => {
-        node.position = { x, y: index * 150 };
-        node.targetPosition = 'left';
-        node.sourcePosition = 'right';
-      });
-    });
-
-    return { nodes, edges };
-  }
-
-  try {
-    const dagreGraph = new window.dagre.graphlib.Graph();
-    dagreGraph.setDefaultEdgeLabel(() => ({}));
-
-    const isHorizontal = direction === 'LR';
-    dagreGraph.setGraph({
-      rankdir: direction,
-      nodesep: 120,   // 垂直间距 - 增加以避免节点重叠
-      ranksep: 280,  // 水平间距 - 增加以容纳更宽的节点
-    });
-
-    // 添加节点 - 根据节点类型设置不同的尺寸
-    nodes.forEach((node) => {
-      let nodeWidth = 280;
-      let nodeHeight = 180;
-
-      // note 节点有轮播图,需要更大的空间
-      if (node.type === 'note') {
-        nodeWidth = 320;
-        nodeHeight = 350;  // 增加高度以容纳轮播图
-      }
-
-      dagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight });
-    });
-
-    // 添加边
-    edges.forEach((edge) => {
-      dagreGraph.setEdge(edge.source, edge.target);
-    });
-
-    // 计算布局
-    window.dagre.layout(dagreGraph);
-    console.log('✅ Dagre layout completed');
-
-    // 更新节点位置和 handle 位置
-    nodes.forEach((node) => {
-      const nodeWithPosition = dagreGraph.node(node.id);
-
-      if (!nodeWithPosition) {
-        console.warn('Node position not found for:', node.id);
-        return;
-      }
-
-      node.targetPosition = isHorizontal ? 'left' : 'top';
-      node.sourcePosition = isHorizontal ? 'right' : 'bottom';
-
-      // 根据节点类型获取尺寸
-      let nodeWidth = 280;
-      let nodeHeight = 180;
-      if (node.type === 'note') {
-        nodeWidth = 320;
-        nodeHeight = 350;
-      }
-
-      // 将 dagre 的中心点位置转换为 React Flow 的左上角位置
-      node.position = {
-        x: nodeWithPosition.x - nodeWidth / 2,
-        y: nodeWithPosition.y - nodeHeight / 2,
-      };
-    });
-
-    console.log('✅ Layout completed, sample node:', nodes[0]);
-    return { nodes, edges };
-  } catch (error) {
-    console.error('❌ Error in dagre layout:', error);
-    console.error('Error details:', error.message, error.stack);
-
-    // 降级处理
-    console.log('Using fallback layout...');
-    const levelGroups = {};
-    nodes.forEach(node => {
-      const level = node.data.level || 0;
-      if (!levelGroups[level]) levelGroups[level] = [];
-      levelGroups[level].push(node);
-    });
-
-    Object.entries(levelGroups).forEach(([level, nodeList]) => {
-      const x = parseInt(level) * 350;
-      nodeList.forEach((node, index) => {
-        node.position = { x, y: index * 150 };
-        node.targetPosition = 'left';
-        node.sourcePosition = 'right';
-      });
-    });
-
-    return { nodes, edges };
-  }
-}
-
-function transformData(data) {
-  const nodes = [];
-  const edges = [];
-
-  const originalIdToCanvasId = {}; // 原始ID -> 画布ID的映射
-  const canvasIdToNodeData = {}; // 避免重复创建相同的节点
-
-  // 创建节点
-  Object.entries(data.nodes).forEach(([originalId, node]) => {
-    if (node.type === 'query') {
-      // 使用 query_level 作为唯一ID
-      const canvasId = node.query + '_' + node.level;
-      originalIdToCanvasId[originalId] = canvasId;
-
-      // 如果这个 canvasId 还没有创建过节点,则创建
-      if (!canvasIdToNodeData[canvasId]) {
-        canvasIdToNodeData[canvasId] = true;
-        nodes.push({
-          id: canvasId, // 使用 query_level 格式
-          originalId: originalId, // 保留原始ID用于调试
-          type: 'query',
-          data: {
-            title: node.query,
-            level: node.level,
-            score: node.relevance_score.toFixed(2),
-            strategy: node.strategy,
-            parent: node.parent_query,
-            isSelected: node.is_selected,
-            evaluationReason: node.evaluation_reason || '',
-          },
-          position: { x: 0, y: 0 }, // 初始位置,会被 dagre 覆盖
-        });
-      }
-    } else if (node.type === 'note') {
-      // note节点直接使用原始ID
-      originalIdToCanvasId[originalId] = originalId;
-
-      if (!canvasIdToNodeData[originalId]) {
-        canvasIdToNodeData[originalId] = true;
-        nodes.push({
-          id: originalId,
-          originalId: originalId,
-          type: 'note',
-          data: {
-            title: node.title,
-            matchLevel: node.match_level,
-            score: node.relevance_score.toFixed(2),
-            description: node.desc,
-            isSelected: node.is_selected !== undefined ? node.is_selected : true,
-            imageList: node.image_list || [], // 添加图片列表
-            noteUrl: node.note_url || '', // 添加帖子链接
-            evaluationReason: node.evaluation_reason || '', // 添加评估理由
-          },
-          position: { x: 0, y: 0 },
-        });
-      }
-    }
-  });
-
-  // 创建边 - 使用虚线样式,映射到画布ID
-  data.edges.forEach((edge, index) => {
-    const edgeColors = {
-      '初始分词': '#10b981',
-      '调用sug': '#06b6d4',
-      '同义改写': '#f59e0b',
-      '加词': '#3b82f6',
-      '抽象改写': '#8b5cf6',
-      '基于部分匹配改进': '#ec4899',
-      '结果分支-抽象改写': '#a855f7',
-      '结果分支-同义改写': '#fb923c',
-      'query_to_note': '#ec4899',
-    };
-
-    const color = edgeColors[edge.strategy] || edgeColors[edge.edge_type] || '#d1d5db';
-    const isNoteEdge = edge.edge_type === 'query_to_note';
-
-    edges.push({
-      id: \`edge-\${index}\`,
-      source: originalIdToCanvasId[edge.from], // 使用画布ID
-      target: originalIdToCanvasId[edge.to],   // 使用画布ID
-      type: 'simplebezier', // 使用简单贝塞尔曲线
-      animated: isNoteEdge,
-      style: {
-        stroke: color,
-        strokeWidth: isNoteEdge ? 2.5 : 2,
-        strokeDasharray: isNoteEdge ? '5,5' : '8,4',
-      },
-      markerEnd: {
-        type: 'arrowclosed',
-        color: color,
-        width: 20,
-        height: 20,
-      },
-    });
-  });
-
-  // 使用 dagre 自动计算布局 - 从左到右
-  return getLayoutedElements(nodes, edges, 'LR');
-}
-
-function FlowContent() {
-  const { nodes: initialNodes, edges: initialEdges } = useMemo(() => {
-    console.log('🔍 Transforming data...');
-    const result = transformData(data);
-    console.log('✅ Transformed:', result.nodes.length, 'nodes,', result.edges.length, 'edges');
-    return result;
-  }, []);
-
-  // 初始化:找出所有有子节点的节点,默认折叠(画布节点)
-  const initialCollapsedNodes = useMemo(() => {
-    const nodesWithChildren = new Set();
-    initialEdges.forEach(edge => {
-      nodesWithChildren.add(edge.source);
-    });
-    // 排除根节点(level 0),让根节点默认展开
-    const rootNode = initialNodes.find(n => n.data.level === 0);
-    if (rootNode) {
-      nodesWithChildren.delete(rootNode.id);
-    }
-    return nodesWithChildren;
-  }, [initialNodes, initialEdges]);
-
-  // 树节点的折叠状态需要在树构建后初始化
-  const [collapsedNodes, setCollapsedNodes] = useState(() => initialCollapsedNodes);
-  const [collapsedTreeNodes, setCollapsedTreeNodes] = useState(new Set());
-  const [selectedNodeId, setSelectedNodeId] = useState(null);
-  const [hiddenNodes, setHiddenNodes] = useState(new Set()); // 用户手动隐藏的节点
-  const [focusMode, setFocusMode] = useState(false); // 全局聚焦模式,默认关闭
-  const [focusedNodeId, setFocusedNodeId] = useState(null); // 单独聚焦的节点ID
-
-  // 获取 React Flow 实例以控制画布
-  const { setCenter, fitView } = useReactFlow();
-
-  // 获取某个节点的所有后代节点ID
-  const getDescendants = useCallback((nodeId) => {
-    const descendants = new Set();
-    const queue = [nodeId];
-
-    while (queue.length > 0) {
-      const current = queue.shift();
-      initialEdges.forEach(edge => {
-        if (edge.source === current && !descendants.has(edge.target)) {
-          descendants.add(edge.target);
-          queue.push(edge.target);
-        }
-      });
-    }
-
-    return descendants;
-  }, [initialEdges]);
-
-  // 获取直接父节点
-  const getDirectParents = useCallback((nodeId) => {
-    const parents = [];
-    initialEdges.forEach(edge => {
-      if (edge.target === nodeId) {
-        parents.push(edge.source);
-      }
-    });
-    return parents;
-  }, [initialEdges]);
-
-  // 获取直接子节点
-  const getDirectChildren = useCallback((nodeId) => {
-    const children = [];
-    initialEdges.forEach(edge => {
-      if (edge.source === nodeId) {
-        children.push(edge.target);
-      }
-    });
-    return children;
-  }, [initialEdges]);
-
-  // 切换节点折叠状态
-  const toggleNodeCollapse = useCallback((nodeId) => {
-    setCollapsedNodes(prev => {
-      const newSet = new Set(prev);
-      const descendants = getDescendants(nodeId);
-
-      if (newSet.has(nodeId)) {
-        // 展开:移除此节点,但保持其他折叠的节点
-        newSet.delete(nodeId);
-      } else {
-        // 折叠:添加此节点
-        newSet.add(nodeId);
-      }
-
-      return newSet;
-    });
-  }, [getDescendants]);
-
-  // 过滤可见的节点和边,并重新计算布局
-  const { nodes, edges } = useMemo(() => {
-    const nodesToHide = new Set();
-
-    // 判断使用哪个节点ID进行聚焦:优先使用单独聚焦的节点,否则使用全局聚焦模式的选中节点
-    const effectiveFocusNodeId = focusedNodeId || (focusMode ? selectedNodeId : null);
-
-    // 聚焦模式:只显示聚焦节点、其父节点和直接子节点
-    if (effectiveFocusNodeId) {
-      const visibleInFocus = new Set([effectiveFocusNodeId]);
-
-      // 添加所有父节点
-      initialEdges.forEach(edge => {
-        if (edge.target === effectiveFocusNodeId) {
-          visibleInFocus.add(edge.source);
-        }
-      });
-
-      // 添加所有直接子节点
-      initialEdges.forEach(edge => {
-        if (edge.source === effectiveFocusNodeId) {
-          visibleInFocus.add(edge.target);
-        }
-      });
-
-      // 隐藏不在聚焦范围内的节点
-      initialNodes.forEach(node => {
-        if (!visibleInFocus.has(node.id)) {
-          nodesToHide.add(node.id);
-        }
-      });
-    } else {
-      // 非聚焦模式:使用原有的折叠逻辑
-      // 收集所有被折叠节点的后代
-      collapsedNodes.forEach(collapsedId => {
-        const descendants = getDescendants(collapsedId);
-        descendants.forEach(id => nodesToHide.add(id));
-      });
-    }
-
-    // 添加用户手动隐藏的节点
-    hiddenNodes.forEach(id => nodesToHide.add(id));
-
-    const visibleNodes = initialNodes
-      .filter(node => !nodesToHide.has(node.id))
-      .map(node => ({
-        ...node,
-        data: {
-          ...node.data,
-          isCollapsed: collapsedNodes.has(node.id),
-          hasChildren: initialEdges.some(e => e.source === node.id),
-          onToggleCollapse: () => toggleNodeCollapse(node.id),
-          onHideSelf: () => {
-            setHiddenNodes(prev => {
-              const newSet = new Set(prev);
-              newSet.add(node.id);
-              return newSet;
-            });
-          },
-          onFocus: () => {
-            // 切换聚焦状态
-            if (focusedNodeId === node.id) {
-              setFocusedNodeId(null); // 如果已经聚焦,则取消聚焦
-            } else {
-              // 先取消之前的聚焦,然后聚焦到当前节点
-              setFocusedNodeId(node.id);
-
-              // 延迟聚焦视图到该节点
-              setTimeout(() => {
-                fitView({
-                  nodes: [{ id: node.id }],
-                  duration: 800,
-                  padding: 0.3,
-                });
-              }, 100);
-            }
-          },
-          isFocused: focusedNodeId === node.id,
-          isHighlighted: selectedNodeId === node.id,
-        }
-      }));
-
-    const visibleEdges = initialEdges.filter(
-      edge => !nodesToHide.has(edge.source) && !nodesToHide.has(edge.target)
-    );
-
-    // 重新计算布局 - 只对可见节点
-    if (typeof window !== 'undefined' && typeof window.dagre !== 'undefined') {
-      try {
-        const dagreGraph = new window.dagre.graphlib.Graph();
-        dagreGraph.setDefaultEdgeLabel(() => ({}));
-
-        dagreGraph.setGraph({
-          rankdir: 'LR',
-          nodesep: 120,   // 垂直间距 - 增加以避免节点重叠
-          ranksep: 280,  // 水平间距 - 增加以容纳更宽的节点
-        });
-
-        visibleNodes.forEach((node) => {
-          let nodeWidth = 280;
-          let nodeHeight = 180;
-
-          // note 节点有轮播图,需要更大的空间
-          if (node.type === 'note') {
-            nodeWidth = 320;
-            nodeHeight = 350;
-          }
-
-          dagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight });
-        });
-
-        visibleEdges.forEach((edge) => {
-          dagreGraph.setEdge(edge.source, edge.target);
-        });
-
-        window.dagre.layout(dagreGraph);
-
-        visibleNodes.forEach((node) => {
-          const nodeWithPosition = dagreGraph.node(node.id);
-          if (nodeWithPosition) {
-            // 根据节点类型获取对应的尺寸
-            let nodeWidth = 280;
-            let nodeHeight = 180;
-            if (node.type === 'note') {
-              nodeWidth = 320;
-              nodeHeight = 350;
-            }
-
-            node.position = {
-              x: nodeWithPosition.x - nodeWidth / 2,
-              y: nodeWithPosition.y - nodeHeight / 2,
-            };
-            node.targetPosition = 'left';
-            node.sourcePosition = 'right';
-          }
-        });
-
-        console.log('✅ Dynamic layout recalculated for', visibleNodes.length, 'visible nodes');
-      } catch (error) {
-        console.error('❌ Error in dynamic layout:', error);
-      }
-    }
-
-    return { nodes: visibleNodes, edges: visibleEdges };
-  }, [initialNodes, initialEdges, collapsedNodes, hiddenNodes, focusMode, focusedNodeId, getDescendants, toggleNodeCollapse, selectedNodeId]);
-
-  // 构建树形结构 - 允许一个节点有多个父节点
-  const buildTree = useCallback(() => {
-    const nodeMap = new Map();
-    initialNodes.forEach(node => {
-      nodeMap.set(node.id, node);
-    });
-
-    // 为每个节点创建树节点的副本(允许多次出现)
-    const createTreeNode = (nodeId, pathKey) => {
-      const node = nodeMap.get(nodeId);
-      if (!node) return null;
-
-      return {
-        ...node,
-        treeKey: pathKey, // 唯一的树路径key,用于React key
-        children: []
-      };
-    };
-
-    // 构建父子关系映射:记录每个节点的所有父节点,去重边
-    const parentToChildren = new Map();
-    const childToParents = new Map();
-
-    initialEdges.forEach(edge => {
-      // 记录父->子关系(去重:同一个父节点到同一个子节点只记录一次)
-      if (!parentToChildren.has(edge.source)) {
-        parentToChildren.set(edge.source, []);
-      }
-      const children = parentToChildren.get(edge.source);
-      if (!children.includes(edge.target)) {
-        children.push(edge.target);
-      }
-
-      // 记录子->父关系(用于判断是否有多个父节点,也去重)
-      if (!childToParents.has(edge.target)) {
-        childToParents.set(edge.target, []);
-      }
-      const parents = childToParents.get(edge.target);
-      if (!parents.includes(edge.source)) {
-        parents.push(edge.source);
-      }
-    });
-
-    // 递归构建树
-    const buildSubtree = (nodeId, pathKey, visitedInPath) => {
-      // 避免循环引用:如果当前路径中已经访问过这个节点,跳过
-      if (visitedInPath.has(nodeId)) {
-        return null;
-      }
-
-      const treeNode = createTreeNode(nodeId, pathKey);
-      if (!treeNode) return null;
-
-      const newVisitedInPath = new Set(visitedInPath);
-      newVisitedInPath.add(nodeId);
-
-      const children = parentToChildren.get(nodeId) || [];
-      treeNode.children = children
-        .map((childId, index) => buildSubtree(childId, pathKey + '-' + childId + '-' + index, newVisitedInPath))
-        .filter(child => child !== null);
-
-      return treeNode;
-    };
-
-    // 找出所有根节点(没有入边的节点)
-    const hasParent = new Set();
-    initialEdges.forEach(edge => {
-      hasParent.add(edge.target);
-    });
-
-    const roots = [];
-    initialNodes.forEach((node, index) => {
-      if (!hasParent.has(node.id)) {
-        const treeNode = buildSubtree(node.id, 'root-' + node.id + '-' + index, new Set());
-        if (treeNode) roots.push(treeNode);
-      }
-    });
-
-    return roots;
-  }, [initialNodes, initialEdges]);
-
-  const treeRoots = useMemo(() => buildTree(), [buildTree]);
-
-  // 初始化树节点折叠状态
-  useEffect(() => {
-    const getAllTreeKeys = (nodes) => {
-      const keys = new Set();
-      const traverse = (node) => {
-        if (node.children && node.children.length > 0) {
-          // 排除根节点
-          if (node.data.level !== 0) {
-            keys.add(node.treeKey);
-          }
-          node.children.forEach(traverse);
-        }
-      };
-      nodes.forEach(traverse);
-      return keys;
-    };
-
-    setCollapsedTreeNodes(getAllTreeKeys(treeRoots));
-  }, [treeRoots]);
-
-  const renderTree = useCallback((treeNodes, level = 0) => {
-    return treeNodes.map(node => {
-      // 使用 treeKey 来区分树中的不同实例
-      const isCollapsed = collapsedTreeNodes.has(node.treeKey);
-      const isSelected = selectedNodeId === node.id;
-
-      return (
-        <TreeNode
-          key={node.treeKey}
-          node={node}
-          level={level}
-          isCollapsed={isCollapsed}
-          isSelected={isSelected}
-          onToggle={() => {
-            setCollapsedTreeNodes(prev => {
-              const newSet = new Set(prev);
-              if (newSet.has(node.treeKey)) {
-                newSet.delete(node.treeKey);
-              } else {
-                newSet.add(node.treeKey);
-              }
-              return newSet;
-            });
-          }}
-          onSelect={() => {
-            const nodeId = node.id;
-
-            // 展开所有祖先节点
-            const ancestorIds = [nodeId];
-            const findAncestors = (id) => {
-              initialEdges.forEach(edge => {
-                if (edge.target === id && !ancestorIds.includes(edge.source)) {
-                  ancestorIds.push(edge.source);
-                  findAncestors(edge.source);
-                }
-              });
-            };
-            findAncestors(nodeId);
-
-            // 如果节点或其祖先被隐藏,先恢复它们
-            setHiddenNodes(prev => {
-              const newSet = new Set(prev);
-              ancestorIds.forEach(id => newSet.delete(id));
-              return newSet;
-            });
-
-            setSelectedNodeId(nodeId);
-
-            // 获取选中节点的直接子节点
-            const childrenIds = [];
-            initialEdges.forEach(edge => {
-              if (edge.source === nodeId) {
-                childrenIds.push(edge.target);
-              }
-            });
-
-            setCollapsedNodes(prev => {
-              const newSet = new Set(prev);
-              // 展开所有祖先节点
-              ancestorIds.forEach(id => newSet.delete(id));
-              // 展开选中节点本身
-              newSet.delete(nodeId);
-              // 展开选中节点的直接子节点
-              childrenIds.forEach(id => newSet.delete(id));
-              return newSet;
-            });
-
-            // 延迟聚焦,等待节点展开和布局重新计算
-            setTimeout(() => {
-              fitView({
-                nodes: [{ id: nodeId }],
-                duration: 800,
-                padding: 0.3,
-              });
-            }, 300);
-          }}
-        >
-          {node.children && node.children.length > 0 && renderTree(node.children, level + 1)}
-        </TreeNode>
-      );
-    });
-  }, [collapsedTreeNodes, selectedNodeId, nodes, setCenter, initialEdges, setCollapsedNodes, fitView]);
-
-  console.log('📊 Rendering with', nodes.length, 'visible nodes and', edges.length, 'visible edges');
-
-  if (nodes.length === 0) {
-    return (
-      <div style={{ padding: 50, color: 'red', fontSize: 20 }}>
-        ERROR: No nodes to display!
-      </div>
-    );
-  }
-
-  return (
-    <div style={{ width: '100vw', height: '100vh', background: '#f9fafb', display: 'flex', flexDirection: 'column' }}>
-      {/* 顶部面包屑导航栏 */}
-      <div style={{
-        minHeight: '48px',
-        maxHeight: '120px',
-        background: 'white',
-        borderBottom: '1px solid #e5e7eb',
-        display: 'flex',
-        alignItems: 'flex-start',
-        padding: '12px 24px',
-        zIndex: 1000,
-        boxShadow: '0 1px 3px rgba(0, 0, 0, 0.05)',
-        flexShrink: 0,
-        overflowY: 'auto',
-      }}>
-        <div style={{ width: '100%' }}>
-          {selectedNodeId ? (
-            <div style={{ fontSize: '12px', color: '#6b7280' }}>
-              {/* 面包屑导航 - 显示所有路径 */}
-              {(() => {
-                const selectedNode = nodes.find(n => n.id === selectedNodeId);
-                if (!selectedNode) return null;
-
-                // 找到所有从根节点到当前节点的路径
-                const findAllPaths = (targetId) => {
-                  const paths = [];
-
-                  const buildPath = (nodeId, currentPath) => {
-                    const node = initialNodes.find(n => n.id === nodeId);
-                    if (!node) return;
-
-                    const newPath = [node, ...currentPath];
-
-                    // 找到所有父节点
-                    const parents = initialEdges.filter(e => e.target === nodeId).map(e => e.source);
-
-                    if (parents.length === 0) {
-                      // 到达根节点
-                      paths.push(newPath);
-                    } else {
-                      // 递归处理所有父节点
-                      parents.forEach(parentId => {
-                        buildPath(parentId, newPath);
-                      });
-                    }
-                  };
-
-                  buildPath(targetId, []);
-                  return paths;
-                };
-
-                const allPaths = findAllPaths(selectedNodeId);
-
-                // 去重:将路径转换为字符串进行比较
-                const uniquePaths = [];
-                const pathStrings = new Set();
-                allPaths.forEach(path => {
-                  const pathString = path.map(n => n.id).join('->');
-                  if (!pathStrings.has(pathString)) {
-                    pathStrings.add(pathString);
-                    uniquePaths.push(path);
-                  }
-                });
-
-                return (
-                  <div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>
-                    {uniquePaths.map((path, pathIndex) => (
-                      <div key={pathIndex} style={{ display: 'flex', alignItems: 'center', gap: '6px', flexWrap: 'wrap' }}>
-                        {pathIndex > 0 && <span style={{ color: '#d1d5db', marginRight: '4px' }}>或</span>}
-                        {path.map((node, index) => {
-                          // 获取节点的 score、strategy 和 isSelected
-                          const nodeScore = node.data.score ? parseFloat(node.data.score) : 0;
-                          const nodeStrategy = node.data.strategy || '';
-                          const strategyColor = getStrategyColor(nodeStrategy);
-                          const nodeIsSelected = node.type === 'note' ? node.data.matchLevel !== 'unsatisfied' : node.data.isSelected !== false;
-
-                          return (
-                          <React.Fragment key={node.id + '-' + index}>
-                            <span
-                              onClick={() => {
-                                const nodeId = node.id;
-
-                                // 找到所有祖先节点
-                                const ancestorIds = [nodeId];
-                                const findAncestors = (id) => {
-                                  initialEdges.forEach(edge => {
-                                    if (edge.target === id && !ancestorIds.includes(edge.source)) {
-                                      ancestorIds.push(edge.source);
-                                      findAncestors(edge.source);
-                                    }
-                                  });
-                                };
-                                findAncestors(nodeId);
-
-                                // 如果节点或其祖先被隐藏,先恢复它们
-                                setHiddenNodes(prev => {
-                                  const newSet = new Set(prev);
-                                  ancestorIds.forEach(id => newSet.delete(id));
-                                  return newSet;
-                                });
-
-                                // 展开目录树中到达该节点的路径
-                                // 需要找到所有包含该节点的树路径的 treeKey,并展开它们的父节点
-                                setCollapsedTreeNodes(prev => {
-                                  const newSet = new Set(prev);
-                                  // 清空所有折叠状态,让目录树完全展开到选中节点
-                                  // 这样可以确保选中节点在目录中可见
-                                  return new Set();
-                                });
-
-                                setSelectedNodeId(nodeId);
-                                setTimeout(() => {
-                                  fitView({
-                                    nodes: [{ id: nodeId }],
-                                    duration: 800,
-                                    padding: 0.3,
-                                  });
-                                }, 100);
-                              }}
-                              style={{
-                                padding: '6px 8px',
-                                borderRadius: '4px',
-                                background: 'white',
-                                border: index === path.length - 1 ? '2px solid #3b82f6' : '1px solid #d1d5db',
-                                color: '#374151',
-                                fontWeight: index === path.length - 1 ? '600' : '400',
-                                width: '180px',
-                                cursor: 'pointer',
-                                transition: 'all 0.2s ease',
-                                position: 'relative',
-                                display: 'inline-flex',
-                                flexDirection: 'column',
-                                gap: '4px',
-                              }}
-                              onMouseEnter={(e) => {
-                                e.currentTarget.style.opacity = '0.8';
-                              }}
-                              onMouseLeave={(e) => {
-                                e.currentTarget.style.opacity = '1';
-                              }}
-                              title={\`\${node.data.title || node.id} (Score: \${nodeScore.toFixed(2)}, Strategy: \${nodeStrategy}, Selected: \${nodeIsSelected})\`}
-                            >
-                              {/* 上半部分:竖线 + 图标 + 文字 + 分数 */}
-                              <div style={{
-                                display: 'flex',
-                                alignItems: 'center',
-                                gap: '6px',
-                              }}>
-                                {/* 策略类型竖线 */}
-                                <div style={{
-                                  width: '3px',
-                                  height: '16px',
-                                  background: strategyColor,
-                                  borderRadius: '2px',
-                                  flexShrink: 0,
-                                }} />
-
-                                {/* 节点类型图标 */}
-                                <span style={{
-                                  fontSize: '11px',
-                                  flexShrink: 0,
-                                }}>
-                                  {node.type === 'note' ? '📝' : '🔍'}
-                                </span>
-
-                                {/* 节点文字 */}
-                                <span style={{
-                                  flex: 1,
-                                  fontSize: '12px',
-                                  color: nodeIsSelected ? '#374151' : '#ef4444',
-                                }}>
-                                  {truncateMiddle(node.data.title || node.id, 18)}
-                                </span>
-
-                                {/* 分数显示 */}
-                                <span style={{
-                                  fontSize: '10px',
-                                  color: '#6b7280',
-                                  fontWeight: '500',
-                                  flexShrink: 0,
-                                }}>
-                                  {nodeScore.toFixed(2)}
-                                </span>
-                              </div>
-
-                              {/* 分数下划线 */}
-                              <div style={{
-                                width: (nodeScore * 100) + '%',
-                                height: '2px',
-                                background: getScoreColor(nodeScore),
-                                borderRadius: '1px',
-                                marginLeft: '9px',
-                              }} />
-                            </span>
-                            {index < path.length - 1 && <span style={{ color: '#9ca3af' }}>›</span>}
-                          </React.Fragment>
-                        )})}
-                      </div>
-                    ))}
-                  </div>
-                );
-              })()}
-            </div>
-          ) : (
-            <div style={{ fontSize: '13px', color: '#9ca3af', textAlign: 'center' }}>
-              选择一个节点查看路径
-            </div>
-          )}
-        </div>
-      </div>
-
-      {/* 主内容区:目录 + 画布 */}
-      <div style={{ display: 'flex', flex: 1, overflow: 'hidden' }}>
-        {/* 左侧目录树 */}
-        <div style={{
-          width: '320px',
-          background: 'white',
-          borderRight: '1px solid #e5e7eb',
-          display: 'flex',
-          flexDirection: 'column',
-          flexShrink: 0,
-        }}>
-          <div style={{
-            padding: '12px 16px',
-            borderBottom: '1px solid #e5e7eb',
-            display: 'flex',
-            justifyContent: 'space-between',
-            alignItems: 'center',
-          }}>
-            <span style={{
-              fontWeight: '600',
-              fontSize: '14px',
-              color: '#111827',
-            }}>
-              节点目录
-            </span>
-            <div style={{ display: 'flex', gap: '6px' }}>
-              <button
-                onClick={() => {
-                  setCollapsedTreeNodes(new Set());
-                }}
-                style={{
-                  fontSize: '11px',
-                  padding: '4px 8px',
-                  borderRadius: '4px',
-                  border: '1px solid #d1d5db',
-                  background: 'white',
-                  color: '#6b7280',
-                  cursor: 'pointer',
-                  fontWeight: '500',
-                }}
-                title="展开全部节点"
-              >
-                全部展开
-              </button>
-              <button
-                onClick={() => {
-                  const getAllTreeKeys = (nodes) => {
-                    const keys = new Set();
-                    const traverse = (node) => {
-                      if (node.children && node.children.length > 0) {
-                        keys.add(node.treeKey);
-                        node.children.forEach(traverse);
-                      }
-                    };
-                    nodes.forEach(traverse);
-                    return keys;
-                  };
-                  setCollapsedTreeNodes(getAllTreeKeys(treeRoots));
-                }}
-                style={{
-                  fontSize: '11px',
-                  padding: '4px 8px',
-                  borderRadius: '4px',
-                  border: '1px solid #d1d5db',
-                  background: 'white',
-                  color: '#6b7280',
-                  cursor: 'pointer',
-                  fontWeight: '500',
-                }}
-                title="折叠全部节点"
-              >
-                全部折叠
-              </button>
-            </div>
-          </div>
-          <div style={{
-            flex: 1,
-            overflowX: 'auto',
-            overflowY: 'auto',
-            padding: '8px',
-          }}>
-            <div style={{ minWidth: 'fit-content' }}>
-              {renderTree(treeRoots)}
-            </div>
-          </div>
-        </div>
-
-        {/* 画布区域 */}
-        <div style={{ flex: 1, position: 'relative' }}>
-
-          {/* 右侧图例 */}
-          <div style={{
-            position: 'absolute',
-            top: '20px',
-            right: '20px',
-            background: 'white',
-            padding: '16px',
-            borderRadius: '12px',
-            boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08)',
-            zIndex: 1000,
-            maxWidth: '260px',
-            border: '1px solid #e5e7eb',
-          }}>
-        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '12px' }}>
-          <h3 style={{ fontSize: '14px', fontWeight: '600', color: '#111827', margin: 0 }}>图例</h3>
-          <button
-            onClick={() => setFocusMode(!focusMode)}
-            style={{
-              fontSize: '11px',
-              padding: '4px 8px',
-              borderRadius: '4px',
-              border: '1px solid',
-              borderColor: focusMode ? '#3b82f6' : '#d1d5db',
-              background: focusMode ? '#3b82f6' : 'white',
-              color: focusMode ? 'white' : '#6b7280',
-              cursor: 'pointer',
-              fontWeight: '500',
-            }}
-            title={focusMode ? '关闭聚焦模式' : '开启聚焦模式'}
-          >
-            {focusMode ? '🎯 聚焦' : '📊 全图'}
-          </button>
-        </div>
-
-        <div style={{ fontSize: '12px' }}>
-          {/* 画布节点展开/折叠控制 */}
-          <div style={{ marginBottom: '12px', paddingBottom: '12px', borderBottom: '1px solid #f3f4f6' }}>
-            <div style={{ fontSize: '12px', fontWeight: '500', marginBottom: '8px', color: '#374151' }}>节点控制</div>
-            <div style={{ display: 'flex', gap: '6px' }}>
-              <button
-                onClick={() => {
-                  setCollapsedNodes(new Set());
-                }}
-                style={{
-                  fontSize: '11px',
-                  padding: '4px 8px',
-                  borderRadius: '4px',
-                  border: '1px solid #d1d5db',
-                  background: 'white',
-                  color: '#6b7280',
-                  cursor: 'pointer',
-                  fontWeight: '500',
-                  flex: 1,
-                }}
-                title="展开画布中所有节点的子节点"
-              >
-                全部展开
-              </button>
-              <button
-                onClick={() => {
-                  const allNodeIds = new Set(initialNodes.map(n => n.id));
-                  setCollapsedNodes(allNodeIds);
-                }}
-                style={{
-                  fontSize: '11px',
-                  padding: '4px 8px',
-                  borderRadius: '4px',
-                  border: '1px solid #d1d5db',
-                  background: 'white',
-                  color: '#6b7280',
-                  cursor: 'pointer',
-                  fontWeight: '500',
-                  flex: 1,
-                }}
-                title="折叠画布中所有节点的子节点"
-              >
-                全部折叠
-              </button>
-            </div>
-          </div>
-
-          <div style={{ paddingTop: '12px', borderTop: '1px solid #f3f4f6' }}>
-            <div style={{ fontSize: '12px', fontWeight: '500', marginBottom: '8px', color: '#374151' }}>策略类型</div>
-            <div style={{ display: 'flex', alignItems: 'center', margin: '6px 0' }}>
-              <div style={{ width: '20px', height: '2px', marginRight: '8px', background: '#10b981', opacity: 0.7 }}></div>
-              <span style={{ color: '#6b7280', fontSize: '11px' }}>初始分词</span>
-            </div>
-            <div style={{ display: 'flex', alignItems: 'center', margin: '6px 0' }}>
-              <div style={{ width: '20px', height: '2px', marginRight: '8px', background: '#06b6d4', opacity: 0.7 }}></div>
-              <span style={{ color: '#6b7280', fontSize: '11px' }}>调用sug</span>
-            </div>
-            <div style={{ display: 'flex', alignItems: 'center', margin: '6px 0' }}>
-              <div style={{ width: '20px', height: '2px', marginRight: '8px', background: '#f59e0b', opacity: 0.7 }}></div>
-              <span style={{ color: '#6b7280', fontSize: '11px' }}>同义改写</span>
-            </div>
-            <div style={{ display: 'flex', alignItems: 'center', margin: '6px 0' }}>
-              <div style={{ width: '20px', height: '2px', marginRight: '8px', background: '#3b82f6', opacity: 0.7 }}></div>
-              <span style={{ color: '#6b7280', fontSize: '11px' }}>加词</span>
-            </div>
-            <div style={{ display: 'flex', alignItems: 'center', margin: '6px 0' }}>
-              <div style={{ width: '20px', height: '2px', marginRight: '8px', background: '#8b5cf6', opacity: 0.7 }}></div>
-              <span style={{ color: '#6b7280', fontSize: '11px' }}>抽象改写</span>
-            </div>
-            <div style={{ display: 'flex', alignItems: 'center', margin: '6px 0' }}>
-              <div style={{ width: '20px', height: '2px', marginRight: '8px', background: '#ec4899', opacity: 0.7 }}></div>
-              <span style={{ color: '#6b7280', fontSize: '11px' }}>基于部分匹配改进</span>
-            </div>
-            <div style={{ display: 'flex', alignItems: 'center', margin: '6px 0' }}>
-              <div style={{ width: '20px', height: '2px', marginRight: '8px', background: '#a855f7', opacity: 0.7 }}></div>
-              <span style={{ color: '#6b7280', fontSize: '11px' }}>结果分支-抽象改写</span>
-            </div>
-            <div style={{ display: 'flex', alignItems: 'center', margin: '6px 0' }}>
-              <div style={{ width: '20px', height: '2px', marginRight: '8px', background: '#fb923c', opacity: 0.7 }}></div>
-              <span style={{ color: '#6b7280', fontSize: '11px' }}>结果分支-同义改写</span>
-            </div>
-          </div>
-
-          <div style={{
-            marginTop: '12px',
-            paddingTop: '12px',
-            borderTop: '1px solid #f3f4f6',
-            fontSize: '11px',
-            color: '#9ca3af',
-            lineHeight: '1.5',
-          }}>
-            💡 点击节点左上角 × 隐藏节点
-          </div>
-
-          {/* 隐藏节点列表 - 在图例内部 */}
-          {hiddenNodes.size > 0 && (
-            <div style={{
-              marginTop: '12px',
-              paddingTop: '12px',
-              borderTop: '1px solid #f3f4f6',
-            }}>
-              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '8px' }}>
-                <h4 style={{ fontSize: '12px', fontWeight: '600', color: '#111827' }}>已隐藏节点</h4>
-                <button
-                  onClick={() => setHiddenNodes(new Set())}
-                  style={{
-                    fontSize: '10px',
-                    color: '#3b82f6',
-                    background: 'none',
-                    border: 'none',
-                    cursor: 'pointer',
-                    textDecoration: 'underline',
-                  }}
-                >
-                  全部恢复
-                </button>
-              </div>
-              <div style={{ fontSize: '12px', maxHeight: '200px', overflow: 'auto' }}>
-                {Array.from(hiddenNodes).map(nodeId => {
-                  const node = initialNodes.find(n => n.id === nodeId);
-                  if (!node) return null;
-                  return (
-                    <div
-                      key={nodeId}
-                      style={{
-                        display: 'flex',
-                        justifyContent: 'space-between',
-                        alignItems: 'center',
-                        padding: '6px 8px',
-                        margin: '4px 0',
-                        background: '#f9fafb',
-                        borderRadius: '6px',
-                        fontSize: '11px',
-                      }}
-                    >
-                      <span
-                        style={{
-                          flex: 1,
-                          overflow: 'hidden',
-                          textOverflow: 'ellipsis',
-                          whiteSpace: 'nowrap',
-                          color: '#374151',
-                        }}
-                        title={node.data.title || nodeId}
-                      >
-                        {node.data.title || nodeId}
-                      </span>
-                      <button
-                        onClick={() => {
-                          setHiddenNodes(prev => {
-                            const newSet = new Set(prev);
-                            newSet.delete(nodeId);
-                            return newSet;
-                          });
-                        }}
-                        style={{
-                          marginLeft: '8px',
-                          fontSize: '10px',
-                          color: '#10b981',
-                          background: 'none',
-                          border: 'none',
-                          cursor: 'pointer',
-                          flexShrink: 0,
-                        }}
-                      >
-                        恢复
-                      </button>
-                    </div>
-                  );
-                })}
-              </div>
-            </div>
-          )}
-          </div>
-          </div>
-
-          {/* React Flow 画布 */}
-          <ReactFlow
-            nodes={nodes}
-            edges={edges}
-            nodeTypes={nodeTypes}
-            fitView
-            fitViewOptions={{ padding: 0.2, duration: 500 }}
-            minZoom={0.1}
-            maxZoom={1.5}
-            nodesDraggable={true}
-            nodesConnectable={false}
-            elementsSelectable={true}
-            defaultEdgeOptions={{
-              type: 'smoothstep',
-            }}
-            proOptions={{ hideAttribution: true }}
-            onNodeClick={(event, clickedNode) => {
-              setSelectedNodeId(clickedNode.id);
-            }}
-          >
-            <Controls style={{ bottom: '20px', left: 'auto', right: '20px' }} />
-            <Background variant="dots" gap={20} size={1} color="#e5e7eb" />
-          </ReactFlow>
-        </div>
-      </div>
-    </div>
-  );
-}
-
-function App() {
-  return (
-    <ReactFlowProvider>
-      <FlowContent />
-    </ReactFlowProvider>
-  );
-}
-
-const root = createRoot(document.getElementById('root'));
-root.render(<App />);
-`;
-
-fs.writeFileSync(reactComponentPath, reactComponent);
-
-// 使用 esbuild 打包
-console.log('🎨 Building modern visualization...');
-
-build({
-  entryPoints: [reactComponentPath],
-  bundle: true,
-  outfile: path.join(__dirname, 'bundle_v2.js'),
-  format: 'iife',
-  loader: {
-    '.css': 'css',
-  },
-  minify: false,
-  sourcemap: 'inline',
-  // 强制所有 React 引用指向同一个位置,避免多副本
-  alias: {
-    'react': path.join(__dirname, 'node_modules/react'),
-    'react-dom': path.join(__dirname, 'node_modules/react-dom'),
-    'react/jsx-runtime': path.join(__dirname, 'node_modules/react/jsx-runtime'),
-    'react/jsx-dev-runtime': path.join(__dirname, 'node_modules/react/jsx-dev-runtime'),
-  },
-}).then(() => {
-  // 读取打包后的 JS
-  const bundleJs = fs.readFileSync(path.join(__dirname, 'bundle_v2.js'), 'utf-8');
-
-  // 读取 CSS
-  const cssPath = path.join(__dirname, 'node_modules/@xyflow/react/dist/style.css');
-  const css = fs.readFileSync(cssPath, 'utf-8');
-
-  // 生成最终 HTML
-  const html = `<!DOCTYPE html>
-<html lang="zh-CN">
-<head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>查询图可视化</title>
-    <link rel="preconnect" href="https://fonts.googleapis.com">
-    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
-    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
-    <script src="https://unpkg.com/dagre@0.8.5/dist/dagre.min.js"></script>
-    <script>
-      // 过滤特定的 React 警告
-      const originalError = console.error;
-      console.error = (...args) => {
-        if (typeof args[0] === 'string' && args[0].includes('Each child in a list should have a unique "key" prop')) {
-          return;
-        }
-        originalError.apply(console, args);
-      };
-    </script>
-    <style>
-        * {
-            margin: 0;
-            padding: 0;
-            box-sizing: border-box;
-        }
-        body {
-            font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
-            overflow: hidden;
-            -webkit-font-smoothing: antialiased;
-            -moz-osx-font-smoothing: grayscale;
-        }
-        #root {
-            width: 100vw;
-            height: 100vh;
-        }
-        ${css}
-
-        /* 自定义样式覆盖 */
-        .react-flow__edge-path {
-            stroke-linecap: round;
-        }
-        .react-flow__controls {
-            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
-            border: 1px solid #e5e7eb;
-            border-radius: 8px;
-        }
-        .react-flow__controls-button {
-            border: none;
-            border-bottom: 1px solid #e5e7eb;
-        }
-        .react-flow__controls-button:hover {
-            background: #f9fafb;
-        }
-    </style>
-</head>
-<body>
-    <div id="root"></div>
-    <script>${bundleJs}</script>
-</body>
-</html>`;
-
-  // 写入输出文件
-  fs.writeFileSync(outputFile, html);
-
-  // 清理临时文件
-  fs.unlinkSync(reactComponentPath);
-  fs.unlinkSync(path.join(__dirname, 'bundle_v2.js'));
-
-  console.log('✅ Visualization generated: ' + outputFile);
-  console.log('📊 Nodes: ' + Object.keys(data.nodes).length);
-  console.log('🔗 Edges: ' + data.edges.length);
-}).catch(error => {
-  console.error('❌ Build error:', error);
-  process.exit(1);
-});

Some files were not shown because too many files changed in this diff