xueyiming il y a 15 heures
Parent
commit
1a2047aba9
36 fichiers modifiés avec 399 ajouts et 83 suppressions
  1. 0 0
      examples/demand/__init__.py
  2. 1 1
      examples/demand/agent_tools.py
  3. 289 0
      examples/demand/changwen_prepare.py
  4. 0 0
      examples/demand/config.py
  5. 43 26
      examples/demand/data_query_tools.py
  6. 0 0
      examples/demand/db_manager.py
  7. 0 0
      examples/demand/demand.md
  8. 2 2
      examples/demand/demand_build_agent_tools.py
  9. 0 0
      examples/demand/log_capture.py
  10. 0 0
      examples/demand/models.py
  11. 0 0
      examples/demand/mysql/README.md
  12. 0 0
      examples/demand/mysql/__init__.py
  13. 0 0
      examples/demand/mysql/db_config.py
  14. 0 0
      examples/demand/mysql/example_usage.py
  15. 0 0
      examples/demand/mysql/mysql_advanced.py
  16. 0 0
      examples/demand/mysql/mysql_exceptions.py
  17. 0 0
      examples/demand/mysql/mysql_helper.py
  18. 0 0
      examples/demand/mysql/mysql_pool.py
  19. 0 0
      examples/demand/mysql/mysql_transaction.py
  20. 0 0
      examples/demand/mysql/test_mysql_utils.py
  21. 0 0
      examples/demand/pattern_builds/__init__.py
  22. 0 0
      examples/demand/pattern_builds/apriori_analysis_post_level.py
  23. 0 0
      examples/demand/pattern_builds/build_item_graph.py
  24. 0 0
      examples/demand/pattern_builds/data_operation.py
  25. 0 0
      examples/demand/pattern_builds/db_manager.py
  26. 0 0
      examples/demand/pattern_builds/models1.py
  27. 0 0
      examples/demand/pattern_builds/models2.py
  28. 0 0
      examples/demand/pattern_builds/pattern_service.py
  29. 0 0
      examples/demand/pattern_builds/post_data_service.py
  30. 0 0
      examples/demand/pattern_service.py
  31. 14 4
      examples/demand/piaoquan_prepare.py
  32. 2 2
      examples/demand/render_log_html.py
  33. 44 44
      examples/demand/run.py
  34. 0 0
      examples/demand/topic_build_agent_context.py
  35. 1 1
      examples/demand/topic_build_pattern_tools.py
  36. 3 3
      examples/demand/weight_score_query_tools.py

+ 0 - 0
examples/piaoquan_demand/__init__.py → examples/demand/__init__.py


+ 1 - 1
examples/piaoquan_demand/agent_tools.py → examples/demand/agent_tools.py

@@ -1,5 +1,5 @@
 from agent.tools import ToolResult, tool
-from examples.piaoquan_demand.topic_build_pattern_tools import _log_tool_input, _log_tool_output
+from examples.demand.topic_build_pattern_tools import _log_tool_input, _log_tool_output
 
 
 @tool(

+ 289 - 0
examples/demand/changwen_prepare.py

@@ -0,0 +1,289 @@
+import json
+from collections import defaultdict
+from pathlib import Path
+
+from examples.demand.data_query_tools import get_changwen_weight
+from examples.demand.db_manager import DatabaseManager
+from examples.demand.models import TopicPatternElement, TopicPatternExecution
+from examples.demand.pattern_builds.pattern_service import run_mining
+
+db = DatabaseManager()
+
+
+def _safe_float(value):
+    if value is None:
+        return 0.0
+    try:
+        return float(value)
+    except (TypeError, ValueError):
+        return 0.0
+
+
+def _build_category_scores(name_scores, name_paths, name_post_ids):
+    node_scores = defaultdict(float)
+    node_post_ids = defaultdict(set)
+    for name, score in name_scores.items():
+        paths = name_paths.get(name, set())
+        post_ids = name_post_ids.get(name, set())
+        for category_path in paths:
+            if not category_path:
+                continue
+            nodes = [segment.strip() for segment in category_path.split(">") if segment.strip()]
+            for idx in range(len(nodes)):
+                prefix = ">".join(nodes[: idx + 1])
+                node_scores[prefix] += score
+                if post_ids:
+                    node_post_ids[prefix].update(post_ids)
+    return node_scores, node_post_ids
+
+
+def _write_json(path, payload):
+    with open(path, "w", encoding="utf-8") as f:
+        json.dump(payload, f, ensure_ascii=False, indent=2)
+
+
+def _normalize_post_id(value):
+    """
+    统一 post_id/videoid 格式,降低源数据格式差异导致的匹配失败。
+    """
+    if value is None:
+        return ""
+    s = str(value).strip()
+    if not s:
+        return ""
+    if s.endswith(".0"):
+        s = s[:-2]
+    return s
+
+
+def _extract_digits(value: str) -> str:
+    if not value:
+        return ""
+    return "".join(ch for ch in value if ch.isdigit())
+
+
+def _build_score_by_videoid(cluster_name: str):
+    json_path = Path(__file__).parent / f"{cluster_name}.json"
+    with open(json_path, "r", encoding="utf-8") as f:
+        payload = json.load(f)
+
+    if not isinstance(payload, list):
+        raise ValueError(f"数据格式错误,期望数组: {json_path}")
+
+    score_map = {}
+    for item in payload:
+        if not isinstance(item, dict):
+            continue
+        videoid = item.get("videoid")
+        if videoid is None:
+            continue
+        ext_data = item.get("ext_data") or {}
+        if not isinstance(ext_data, dict):
+            continue
+        realplay = _safe_float(ext_data.get("推荐realplay"))
+        exposure = _safe_float(ext_data.get("推荐曝光数"))
+        norm_videoid = _normalize_post_id(videoid)
+        if not norm_videoid:
+            continue
+        score_map[norm_videoid] = (realplay / exposure) if exposure > 0 else 0.0
+    return score_map
+
+
+def filter_low_exposure_records(
+        cluster_name: str = None,
+        min_exposure: float = 1000,
+):
+    """
+    过滤 JSON 中推荐曝光数小于阈值的记录,并写回原文件。
+    默认过滤阈值: 1000
+    """
+    json_path = Path(__file__).parent / f"{cluster_name}.json"
+    with open(json_path, "r", encoding="utf-8") as f:
+        payload = json.load(f)
+
+    if not isinstance(payload, list):
+        raise ValueError(f"数据格式错误,期望数组: {json_path}")
+
+    filtered = []
+    for item in payload:
+        if not isinstance(item, dict):
+            continue
+        ext_data = item.get("ext_data") or {}
+        exposure = _safe_float(ext_data.get("推荐曝光数")) if isinstance(ext_data, dict) else 0.0
+        if exposure >= float(min_exposure):
+            filtered.append(item)
+
+    with open(json_path, "w", encoding="utf-8") as f:
+        json.dump(filtered, f, ensure_ascii=False, indent=2)
+
+    return {
+        "file": str(json_path),
+        "before_count": len(payload),
+        "after_count": len(filtered),
+        "removed_count": len(payload) - len(filtered),
+        "min_exposure": float(min_exposure),
+    }
+
+
+def changwen_data_prepare(cluster_name) -> int:
+    json_path = Path(__file__).parent / f"{cluster_name}.json"
+    with open(json_path, "r", encoding="utf-8") as f:
+        payload = json.load(f)
+
+    if not isinstance(payload, list):
+        raise ValueError(f"数据格式错误,期望数组: {json_path}")
+
+    video_ids = []
+    for item in payload:
+        if not isinstance(item, dict):
+            continue
+        video_id = item.get("videoid")
+        if video_id is None:
+            continue
+        video_id_str = str(video_id).strip()
+        if video_id_str:
+            video_ids.append(video_id_str)
+
+    # 去重并保持原有顺序,避免重复挖掘同一视频
+    video_ids = list(dict.fromkeys(video_ids))
+    if not video_ids:
+        raise ValueError(f"未在文件中解析到有效 videoid: {json_path}")
+
+    execution_id = run_mining(post_ids=video_ids, cluster_name=cluster_name)
+    return execution_id
+
+
+def prepare_by_json_score(execution_id: int, cluster_name: str = "奇观妙技有乾坤"):
+    """
+    与 prepare.py 的输出结构保持一致,但分数来源改为:
+    score = 推荐realplay / 推荐曝光数
+    """
+    session = db.get_session()
+    try:
+        execution = session.query(TopicPatternExecution).filter(
+            TopicPatternExecution.id == execution_id
+        ).first()
+        if not execution:
+            raise ValueError(f"execution_id 不存在: {execution_id}")
+
+        score_by_post_id = _build_score_by_videoid(cluster_name)
+        rows = session.query(TopicPatternElement).filter(
+            TopicPatternElement.execution_id == execution_id
+        ).all()
+        if not rows:
+            return {"message": "没有可处理的数据", "execution_id": execution_id}
+
+        grouped = {
+            "实质": {"name_post_ids": defaultdict(set), "name_paths": defaultdict(set)},
+            "形式": {"name_post_ids": defaultdict(set), "name_paths": defaultdict(set)},
+            "意图": {"name_post_ids": defaultdict(set), "name_paths": defaultdict(set)},
+        }
+
+        for r in rows:
+            element_type = (r.element_type or "").strip()
+            if element_type not in grouped:
+                continue
+            name = (r.name or "").strip()
+            if not name:
+                continue
+            if r.post_id:
+                grouped[element_type]["name_post_ids"][name].add(str(r.post_id))
+            if r.category_path:
+                grouped[element_type]["name_paths"][name].add(r.category_path.strip())
+
+        output_dir = Path(__file__).parent / "data" / str(execution_id)
+        output_dir.mkdir(parents=True, exist_ok=True)
+        match_stats = {
+            "post_ids_total": 0,
+            "post_ids_scored_direct": 0,
+            "post_ids_scored_by_digits": 0,
+            "post_ids_missing_score": 0,
+        }
+        summary = {
+            "execution_id": execution_id,
+            "merge_leve2": execution.merge_leve2,
+            "files": {},
+            "score_match_stats": match_stats,
+        }
+
+        for element_type, data in grouped.items():
+            name_post_ids = data["name_post_ids"]
+            name_paths = data["name_paths"]
+
+            name_scores = {}
+            for name, post_ids in name_post_ids.items():
+                scores = []
+                for raw_pid in post_ids:
+                    match_stats["post_ids_total"] += 1
+                    pid = _normalize_post_id(raw_pid)
+                    score = score_by_post_id.get(pid)
+                    if score is not None:
+                        match_stats["post_ids_scored_direct"] += 1
+                        scores.append(_safe_float(score))
+                        continue
+
+                    # 兜底:当 post_id 含前后缀时,尝试仅用数字部分匹配 videoid
+                    digits_pid = _extract_digits(pid)
+                    if digits_pid and digits_pid in score_by_post_id:
+                        match_stats["post_ids_scored_by_digits"] += 1
+                        scores.append(_safe_float(score_by_post_id[digits_pid]))
+                    else:
+                        match_stats["post_ids_missing_score"] += 1
+                        scores.append(0.0)
+                name_scores[name] = (sum(scores) / len(scores)) if scores else 0.0
+
+            raw_elements = []
+            for name, score in name_scores.items():
+                post_ids_set = name_post_ids.get(name, set())
+                raw_elements.append(
+                    {
+                        "name": name,
+                        "score": round(score, 6),
+                        "post_ids_count": len(post_ids_set),
+                        "category_paths": sorted(list(name_paths.get(name, set()))),
+                    }
+                )
+
+            element_payload = sorted(raw_elements, key=lambda x: (-x["score"], x["name"]))
+            category_scores, category_post_ids = _build_category_scores(
+                name_scores, name_paths, name_post_ids
+            )
+            category_payload = sorted(
+                [
+                    {
+                        "category_path": path,
+                        "category": path.split(">")[-1].strip() if path else "",
+                        "score": round(score, 6),
+                        "post_ids_count": len(category_post_ids.get(path, set())),
+                    }
+                    for path, score in category_scores.items()
+                ],
+                key=lambda x: x["score"],
+                reverse=True,
+            )
+
+            element_file = output_dir / f"{element_type}_元素.json"
+            category_file = output_dir / f"{element_type}_分类.json"
+            _write_json(element_file, element_payload)
+            _write_json(category_file, category_payload)
+            summary["files"][f"{element_type}_元素"] = str(element_file)
+            summary["files"][f"{element_type}_分类"] = str(category_file)
+
+        return summary
+    finally:
+        session.close()
+
+def changwen_prepare(cluster_name):
+    get_changwen_weight(cluster_name)
+    filter_low_exposure_records(cluster_name=cluster_name)
+    execution_id = changwen_data_prepare(cluster_name)
+    print(f"execution_id={execution_id}")
+    print(prepare_by_json_score(execution_id, cluster_name))
+    return execution_id
+
+
+if __name__ == "__main__":
+    cluster_name = '小阳看天下'
+    execution_id = changwen_prepare(cluster_name=cluster_name)
+    print(execution_id)
+

+ 0 - 0
examples/piaoquan_demand/config.py → examples/demand/config.py


+ 43 - 26
examples/piaoquan_demand/data_query_tools.py → examples/demand/data_query_tools.py

@@ -1,6 +1,8 @@
 from odps import ODPS
 from odps.errors import ODPSError
 from datetime import date, timedelta
+import json
+from pathlib import Path
 
 from agent import tool
 
@@ -79,15 +81,17 @@ def get_changwen_weight(account_name):
     sql_query = f'''
     SELECT  公众号名
         ,videoid
-        ,sum(头部曝光) as 头部曝光
-        ,sum(头部realplay) as 头部realplay
-        ,sum(头部分享) as 头部分享
-        ,sum(头部回流人数) AS 头部回流数
-        ,sum(推荐曝光数) as 推荐曝光数
-        ,sum(推荐realplay) as 推荐realplay
-        ,sum(推荐分享数) as 推荐分享数
-        ,sum(推荐回流数) as 推荐回流数
-        ,sum(当日回流进入分发曝光次数) AS vov分子
+        ,一级品类
+        ,二级品类
+        ,头部曝光
+        ,头部realplay
+        ,头部分享
+        ,头部回流人数 AS 头部回流数
+        ,推荐曝光数
+        ,推荐realplay
+        ,推荐分享数
+        ,推荐回流数
+        ,当日回流进入分发曝光次数 AS vov分子
 FROM    (
             SELECT  DISTINCT a.公众号名
                     ,a.videoid
@@ -146,7 +150,7 @@ FROM    (
                                     FROM    loghubods.video_action_log_rp a
                                     LEFT JOIN loghubods.user_wechat_identity_info_ha b
                                     ON      a.mid = CONCAT('weixin_openid_',b.open_id)
-                                        AND     b.dt = MAX_PT("loghubods.user_wechat_identity_info_ha")
+                                    AND     b.dt = MAX_PT("loghubods.user_wechat_identity_info_ha")
                                     LEFT JOIN loghubods.gzh_fans_info d
                                     ON      b.union_id = d.union_id
                                     AND     d.dt = MAX_PT("loghubods.gzh_fans_info")
@@ -157,6 +161,15 @@ FROM    (
                                     AND     businesstype IN ('videoView','videoPlay','videoShareFriend')
                                     AND     d.user_create_time IS NOT NULL
                                     AND     account_name = '{account_name}'
+                                    AND a.videoid IN (
+                                        SELECT 
+                                        DISTINCT content_id AS videoid
+                                        FROM
+                                        videoods.content_profile
+                                        WHERE status=3
+                                        AND is_deleted = 0
+                                    
+                                    )
                                 ) t
                         GROUP BY 公众号名
                                  ,videoid
@@ -259,7 +272,7 @@ FROM    (
                                         FROM    loghubods.user_share_log a
                                         LEFT JOIN loghubods.user_wechat_identity_info_ha b
                                         ON      a.machinecode = CONCAT('weixin_openid_',b.open_id)
-                                             AND     b.dt = MAX_PT("loghubods.user_wechat_identity_info_ha")
+                                        AND     b.dt = MAX_PT("loghubods.user_wechat_identity_info_ha")
                                         LEFT JOIN loghubods.gzh_fans_info d
                                         ON      b.union_id = d.union_id
                                         AND     d.dt = MAX_PT("loghubods.gzh_fans_info")
@@ -288,35 +301,39 @@ FROM    (
             LEFT JOIN loghubods.video_merge_tag e
             ON      a.videoid = e.videoid
         ) 
-GROUP BY 公众号名, videoid
 ORDER BY 推荐曝光数 DESC
     '''
     result_list = []
     data = get_odps_data(sql_query)
     if data:
-        ext_keys = [
-            "头部曝光",
-            "头部realplay",
-            "头部分享",
-            "头部回流数",
-            "推荐曝光数",
-            "推荐realplay",
-            "推荐分享数",
-            "推荐回流数",
-            "vov分子",
-        ]
         for r in data:
-            ext_data = {k: r[i + 2] for i, k in enumerate(ext_keys)}
             result_list.append(
                 {
                     "account_name": r[0],
                     "videoid": r[1],
-                    "ext_data": ext_data,
+                    "一级品类": r[2],
+                    "二级品类": r[3],
+                    "ext_data": {
+                        "头部曝光": r[4],
+                        "头部realplay": r[5],
+                        "头部分享": r[6],
+                        "头部回流数": r[7],
+                        "推荐曝光数": r[8],
+                        "推荐realplay": r[9],
+                        "推荐分享数": r[10],
+                        "推荐回流数": r[11],
+                        "vov分子": r[12],
+                    },
                 }
             )
+
+    output_file = Path(__file__).parent / f"{account_name}.json"
+    with output_file.open("w", encoding="utf-8") as f:
+        json.dump(result_list, f, ensure_ascii=False, indent=2)
+
     return result_list
 
 
 if __name__ == '__main__':
-    result_list = get_changwen_weight('史趣探秘')
+    result_list = get_changwen_weight('青史铁事漫谈')
     print(result_list)

+ 0 - 0
examples/piaoquan_demand/db_manager.py → examples/demand/db_manager.py


+ 0 - 0
examples/piaoquan_demand/demand.md → examples/demand/demand.md


+ 2 - 2
examples/piaoquan_demand/demand_build_agent_tools.py → examples/demand/demand_build_agent_tools.py

@@ -3,8 +3,8 @@ from pathlib import Path
 from typing import Any, Dict, List, Optional
 
 from agent import tool
-from examples.piaoquan_demand.topic_build_agent_context import TopicBuildAgentContext
-from examples.piaoquan_demand.topic_build_pattern_tools import _log_tool_output, _log_tool_input
+from examples.demand.topic_build_agent_context import TopicBuildAgentContext
+from examples.demand.topic_build_pattern_tools import _log_tool_output, _log_tool_input
 
 def _get_result_base_dir() -> Path:
     """输出到“当前工作目录/result/”下。"""

+ 0 - 0
examples/piaoquan_demand/log_capture.py → examples/demand/log_capture.py


+ 0 - 0
examples/piaoquan_demand/models.py → examples/demand/models.py


+ 0 - 0
examples/piaoquan_demand/mysql/README.md → examples/demand/mysql/README.md


+ 0 - 0
examples/piaoquan_demand/mysql/__init__.py → examples/demand/mysql/__init__.py


+ 0 - 0
examples/piaoquan_demand/mysql/db_config.py → examples/demand/mysql/db_config.py


+ 0 - 0
examples/piaoquan_demand/mysql/example_usage.py → examples/demand/mysql/example_usage.py


+ 0 - 0
examples/piaoquan_demand/mysql/mysql_advanced.py → examples/demand/mysql/mysql_advanced.py


+ 0 - 0
examples/piaoquan_demand/mysql/mysql_exceptions.py → examples/demand/mysql/mysql_exceptions.py


+ 0 - 0
examples/piaoquan_demand/mysql/mysql_helper.py → examples/demand/mysql/mysql_helper.py


+ 0 - 0
examples/piaoquan_demand/mysql/mysql_pool.py → examples/demand/mysql/mysql_pool.py


+ 0 - 0
examples/piaoquan_demand/mysql/mysql_transaction.py → examples/demand/mysql/mysql_transaction.py


+ 0 - 0
examples/piaoquan_demand/mysql/test_mysql_utils.py → examples/demand/mysql/test_mysql_utils.py


+ 0 - 0
examples/piaoquan_demand/pattern_toos/__init__.py → examples/demand/pattern_builds/__init__.py


+ 0 - 0
examples/piaoquan_demand/pattern_toos/apriori_analysis_post_level.py → examples/demand/pattern_builds/apriori_analysis_post_level.py


+ 0 - 0
examples/piaoquan_demand/pattern_toos/build_item_graph.py → examples/demand/pattern_builds/build_item_graph.py


+ 0 - 0
examples/piaoquan_demand/pattern_toos/data_operation.py → examples/demand/pattern_builds/data_operation.py


+ 0 - 0
examples/piaoquan_demand/pattern_toos/db_manager.py → examples/demand/pattern_builds/db_manager.py


+ 0 - 0
examples/piaoquan_demand/pattern_toos/models1.py → examples/demand/pattern_builds/models1.py


+ 0 - 0
examples/piaoquan_demand/pattern_toos/models2.py → examples/demand/pattern_builds/models2.py


+ 0 - 0
examples/piaoquan_demand/pattern_toos/pattern_service.py → examples/demand/pattern_builds/pattern_service.py


+ 0 - 0
examples/piaoquan_demand/pattern_toos/post_data_service.py → examples/demand/pattern_builds/post_data_service.py


+ 0 - 0
examples/piaoquan_demand/pattern_service.py → examples/demand/pattern_service.py


+ 14 - 4
examples/piaoquan_demand/prepare.py → examples/demand/piaoquan_prepare.py

@@ -2,9 +2,12 @@ import json
 from collections import defaultdict
 from pathlib import Path
 
-from examples.piaoquan_demand.data_query_tools import get_rov_by_merge_leve2_and_video_ids
-from examples.piaoquan_demand.db_manager import DatabaseManager
-from examples.piaoquan_demand.models import TopicPatternElement, TopicPatternExecution
+from examples.demand.data_query_tools import get_rov_by_merge_leve2_and_video_ids
+from examples.demand.db_manager import DatabaseManager
+from examples.demand.models import TopicPatternElement, TopicPatternExecution
+from examples.demand.pattern_builds.pattern_service import run_mining
+
+from examples.demand.run import get_execution_id_by_merge_level2
 
 db = DatabaseManager()
 
@@ -160,6 +163,13 @@ def prepare(execution_id):
     finally:
         session.close()
 
+def piaoquan_prepare(cluster_name):
+    execution_id = run_mining(cluster_name=cluster_name, merge_leve2=cluster_name)
+    prepare(execution_id)
+    return execution_id
 
 if __name__ == '__main__':
-    prepare(29)
+    cluster_name = '历史名人'
+    execution_id = piaoquan_prepare(cluster_name=cluster_name)
+    print(execution_id)
+

+ 2 - 2
examples/piaoquan_demand/render_log_html.py → examples/demand/render_log_html.py

@@ -2,7 +2,7 @@
 """将 run_log 文本渲染为可折叠 HTML 页面。
 
 直接在脚本内修改 INPUT_LOG_PATH / OUTPUT_HTML_PATH 后运行:
-    python examples/piaoquan_demand/render_log_html.py
+    python examples/demand/render_log_html.py
 """
 
 from __future__ import annotations
@@ -73,7 +73,7 @@ TOOL_DESCRIPTION_MAP: dict[str, str] = {
 # =========================
 # 运行配置(直接改变量即可)
 # =========================
-INPUT_LOG_PATH = "examples/piaoquan_demand/output/27/run_log_27_20260326_172851.txt"
+INPUT_LOG_PATH = "examples/demand/output/12/run_log_12_20260327_205003.txt"
 # 设为 None 则默认生成到输入文件同名 .html
 OUTPUT_HTML_PATH: str | None = None
 # 是否默认折叠所有 [FOLD] 块

+ 44 - 44
examples/piaoquan_demand/run.py → examples/demand/run.py

@@ -1,4 +1,4 @@
-"""piaoquan_demand 示例的最小可运行入口。"""
+"""demand 示例的最小可运行入口。"""
 
 import asyncio
 import copy
@@ -13,13 +13,13 @@ from typing import Optional
 from dotenv import load_dotenv
 from sqlalchemy import desc, or_
 
-from examples.piaoquan_demand.config import LOG_LEVEL, ENABLED_TOOLS
-from examples.piaoquan_demand.db_manager import DatabaseManager
-from examples.piaoquan_demand.models import TopicPatternExecution
-from examples.piaoquan_demand.pattern_toos.pattern_service import run_mining
-from examples.piaoquan_demand.prepare import prepare
-from examples.piaoquan_demand.topic_build_agent_context import TopicBuildAgentContext
-from examples.piaoquan_demand.mysql import mysql_db
+from examples.demand.changwen_prepare import changwen_prepare
+from examples.demand.config import LOG_LEVEL, ENABLED_TOOLS
+from examples.demand.db_manager import DatabaseManager
+from examples.demand.models import TopicPatternExecution
+from examples.demand.piaoquan_prepare import prepare, piaoquan_prepare
+from examples.demand.topic_build_agent_context import TopicBuildAgentContext
+from examples.demand.mysql import mysql_db
 
 # Clash Verge TUN 模式兼容:禁止 httpx/urllib 自动检测系统 HTTP 代理
 os.environ.setdefault("no_proxy", "*")
@@ -44,27 +44,27 @@ from agent.utils import setup_logging
 from log_capture import build_log, log
 
 # 导入项目配置
-from examples.piaoquan_demand.config import DEBUG, LOG_FILE, LOG_LEVEL, RUN_CONFIG, TRACE_STORE_PATH
+from examples.demand.config import DEBUG, LOG_FILE, LOG_LEVEL, RUN_CONFIG, TRACE_STORE_PATH
 
 CUSTOM_TOOL_MODULES = {
-    # piaoquan_demand 示例:严格按工具名白名单加载对应模块
-    "think_and_plan": "examples.piaoquan_demand.agent_tools",
-    "get_category_tree": "examples.piaoquan_demand.topic_build_pattern_tools",
-    "get_frequent_itemsets": "examples.piaoquan_demand.topic_build_pattern_tools",
-    "get_itemset_detail": "examples.piaoquan_demand.topic_build_pattern_tools",
-    "get_post_elements": "examples.piaoquan_demand.topic_build_pattern_tools",
-    "search_elements": "examples.piaoquan_demand.topic_build_pattern_tools",
-    "get_element_category_chain": "examples.piaoquan_demand.topic_build_pattern_tools",
-    "get_category_detail": "examples.piaoquan_demand.topic_build_pattern_tools",
-    "search_categories": "examples.piaoquan_demand.topic_build_pattern_tools",
-    "get_category_elements": "examples.piaoquan_demand.topic_build_pattern_tools",
-    "get_category_co_occurrences": "examples.piaoquan_demand.topic_build_pattern_tools",
-    "get_element_co_occurrences": "examples.piaoquan_demand.topic_build_pattern_tools",
-    "get_weight_score_topn": "examples.piaoquan_demand.weight_score_query_tools",
-    "get_weight_score_by_name": "examples.piaoquan_demand.weight_score_query_tools",
-    "create_demand_item": "examples.piaoquan_demand.demand_build_agent_tools",
-    "create_demand_items": "examples.piaoquan_demand.demand_build_agent_tools",
-    "write_execution_summary": "examples.piaoquan_demand.demand_build_agent_tools",
+    # demand 示例:严格按工具名白名单加载对应模块
+    "think_and_plan": "examples.demand.agent_tools",
+    "get_category_tree": "examples.demand.topic_build_pattern_tools",
+    "get_frequent_itemsets": "examples.demand.topic_build_pattern_tools",
+    "get_itemset_detail": "examples.demand.topic_build_pattern_tools",
+    "get_post_elements": "examples.demand.topic_build_pattern_tools",
+    "search_elements": "examples.demand.topic_build_pattern_tools",
+    "get_element_category_chain": "examples.demand.topic_build_pattern_tools",
+    "get_category_detail": "examples.demand.topic_build_pattern_tools",
+    "search_categories": "examples.demand.topic_build_pattern_tools",
+    "get_category_elements": "examples.demand.topic_build_pattern_tools",
+    "get_category_co_occurrences": "examples.demand.topic_build_pattern_tools",
+    "get_element_co_occurrences": "examples.demand.topic_build_pattern_tools",
+    "get_weight_score_topn": "examples.demand.weight_score_query_tools",
+    "get_weight_score_by_name": "examples.demand.weight_score_query_tools",
+    "create_demand_item": "examples.demand.demand_build_agent_tools",
+    "create_demand_items": "examples.demand.demand_build_agent_tools",
+    "write_execution_summary": "examples.demand.demand_build_agent_tools",
 }
 
 
@@ -281,8 +281,8 @@ def write_demand_items_to_mysql(execution_id: int, merge_level2: str) -> int:
 
 
 async def run_once(execution_id, merge_level2) -> str:
-    task_id = _create_demand_task(execution_id=execution_id)
-    task_status = 2
+    # task_id = _create_demand_task(execution_id=execution_id)
+    # task_status = 2
     task_log_text = ""
 
     TopicBuildAgentContext.set_execution_id(execution_id)
@@ -356,10 +356,10 @@ async def run_once(execution_id, merge_level2) -> str:
 
             # agent 执行完成后:把本地 result JSON 写入 MySQL 表 demand_content
             # element_names -> name(逗号分隔);reason/desc -> ext_data JSON;merge_leve2 -> demand_content.merge_leve2
-            try:
-                write_demand_items_to_mysql(execution_id=execution_id, merge_level2=merge_level2)
-            except Exception as e:
-                log(f"[mysql] 写入 demand_content 异常:{e}")
+            # try:
+            #     write_demand_items_to_mysql(execution_id=execution_id, merge_level2=merge_level2)
+            # except Exception as e:
+            #     log(f"[mysql] 写入 demand_content 异常:{e}")
 
             task_log_text = log_buffer.getvalue()
             with open(log_file_path, "w", encoding="utf-8") as f:
@@ -371,21 +371,21 @@ async def run_once(execution_id, merge_level2) -> str:
             task_log_text = f"[run] 执行异常: {e}"
         task_status = 2
         raise
-    finally:
-        _finish_demand_task(task_id=task_id, status=task_status, task_log=task_log_text)
+    # finally:
+    # _finish_demand_task(task_id=task_id, status=task_status, task_log=task_log_text)
 
     return final_text
 
 
-async def main() -> None:
-    cluster_name = '历史名人'
-    execution_id = get_execution_id_by_merge_level2(cluster_name=cluster_name)
-    if execution_id is None:
-        execution_id = run_mining(cluster_name=cluster_name, merge_leve2=cluster_name)
-    print(execution_id)
-
-    # await run_once(execution_id, cluster_name)
+async def main(cluster_name, platform_type) -> None:
+    execution_id = None
+    if platform_type == "piaoquan":
+        execution_id = piaoquan_prepare(cluster_name)
+    elif platform_type == "changwen":
+        execution_id = changwen_prepare(cluster_name)
+    if execution_id:
+        await run_once(execution_id, cluster_name)
 
 
 if __name__ == "__main__":
-    asyncio.run(main())
+    asyncio.run(main('小阳看天下.json', 'changwen'))

+ 0 - 0
examples/piaoquan_demand/topic_build_agent_context.py → examples/demand/topic_build_agent_context.py


+ 1 - 1
examples/piaoquan_demand/topic_build_pattern_tools.py → examples/demand/topic_build_pattern_tools.py

@@ -23,7 +23,7 @@ import json
 from typing import Any
 
 from agent import tool
-from examples.piaoquan_demand.topic_build_agent_context import TopicBuildAgentContext
+from examples.demand.topic_build_agent_context import TopicBuildAgentContext
 from log_capture import log
 import pattern_service
 

+ 3 - 3
examples/piaoquan_demand/weight_score_query_tools.py → examples/demand/weight_score_query_tools.py

@@ -3,15 +3,15 @@
 """
 权重分查询工具
 
-从 examples/piaoquan_demand/data/{execution_id} 目录读取权重分 JSON,
+从 examples/demand/data/{execution_id} 目录读取权重分 JSON,
 支持按元素/分类查询 TopN,以及按名称列表批量查询权重分。
 """
 import json
 from pathlib import Path
 
 from agent import tool
-from examples.piaoquan_demand.topic_build_agent_context import TopicBuildAgentContext
-from examples.piaoquan_demand.topic_build_pattern_tools import _log_tool_input, _log_tool_output
+from examples.demand.topic_build_agent_context import TopicBuildAgentContext
+from examples.demand.topic_build_pattern_tools import _log_tool_input, _log_tool_output
 
 _VALID_LEVELS = {"元素", "分类"}
 _VALID_DIMENSIONS = {"实质", "形式", "意图"}