Spaces:
Runtime error
Runtime error
Download src/main.py from BinbomJared/DM-Agent: direct link, hf CLI and curl.
- Browser
- Download file 9.19 kB
-
https://huggingface.co/spaces/BinbomJared/DM-Agent/resolve/main/src/main.py
- Command line
-
hf download hf://spaces/BinbomJared/DM-Agent/src/main.py
-
curl -L -o main.py https://huggingface.co/spaces/BinbomJared/DM-Agent/resolve/main/src/main.py
9.19 kB
| """ | |
| 入口: | |
| --web 网页界面(推荐):浏览器打开,点击即用 | |
| --chat 终端交互模式(默认仍会推钉钉,加 --no-ding 仅本机预览) | |
| --run-once 单次分析最新一周并推送(加 --no-ding 仅打印,不调钉钉) | |
| --dry-run / --preview 只生成报告输出到终端(不推送) | |
| (无参数) 常驻定时任务 | |
| """ | |
| import argparse | |
| import logging | |
| import sys | |
| from pathlib import Path | |
| _root = Path(__file__).resolve().parent.parent | |
| if str(_root) not in sys.path: | |
| sys.path.insert(0, str(_root)) | |
| from src.analyzer import run_analysis, run_monthly_analysis | |
| from src.dingtalk import send_markdown | |
| from src.formatter import ( | |
| format_report, format_title, | |
| format_monthly_report, format_monthly_title, | |
| ) | |
| from src.loader import load_config, load_weekly_data, get_available_weeks | |
| from src.parser import parse_query, describe_query, HELP_TEXT | |
| logging.basicConfig( | |
| level=logging.INFO, | |
| format="%(asctime)s [%(levelname)s] %(message)s", | |
| handlers=[ | |
| logging.StreamHandler(sys.stdout), | |
| logging.FileHandler(_root / "logs" / "agent.log", encoding="utf-8"), | |
| ], | |
| ) | |
| log = logging.getLogger("ad-agent") | |
| def _configure_stdout_utf8() -> None: | |
| """避免 Windows 控制台默认 GBK 打印周报里的 emoji 时崩溃。""" | |
| try: | |
| if hasattr(sys.stdout, "reconfigure"): | |
| sys.stdout.reconfigure(encoding="utf-8", errors="replace") | |
| except Exception: | |
| pass | |
| def _do_analysis(params: dict, config_path: Path) -> tuple[str, str]: | |
| """根据解析参数执行分析,返回 (title, markdown_body)。""" | |
| mode = params["mode"] | |
| if mode == "month": | |
| result = run_monthly_analysis( | |
| config_path=config_path, | |
| target_year=params["year"], | |
| target_month=params["month"], | |
| ) | |
| if result.get("error"): | |
| return "数据不足", result["error"] | |
| return format_monthly_title(result), format_monthly_report(result) | |
| year, week = None, None | |
| if mode == "week": | |
| year, week = params["year"], params["week"] | |
| elif mode == "prev": | |
| df = load_weekly_data(config_path) | |
| weeks = get_available_weeks(df) | |
| if len(weeks) >= 2: | |
| year, week = weeks[-2] | |
| result = run_analysis(config_path=config_path, target_year=year, target_week=week) | |
| return format_title(result), format_report(result) | |
| def run_once( | |
| config_path: Path | None = None, | |
| target_year: int | None = None, | |
| target_week: int | None = None, | |
| *, | |
| send_ding: bool = True, | |
| ) -> bool: | |
| config_path = config_path or _root / "config.yaml" | |
| log.info("开始周度分析...") | |
| try: | |
| result = run_analysis( | |
| config_path=config_path, | |
| target_year=target_year, | |
| target_week=target_week, | |
| ) | |
| except Exception as e: | |
| log.error(f"分析失败: {e}", exc_info=True) | |
| return False | |
| title = format_title(result) | |
| text = format_report(result) | |
| if not send_ding: | |
| print("\n" + "=" * 64) | |
| print(f"【终端预览 · 未推送钉钉】{title}") | |
| print("=" * 64 + "\n") | |
| print(text) | |
| print("\n" + "=" * 64) | |
| log.info("已输出到终端(预览模式,未调用钉钉)。") | |
| return True | |
| ok, err = send_markdown(title=title, text=text) | |
| if not ok: | |
| log.error(f"钉钉发送失败: {err}") | |
| print(text) | |
| else: | |
| log.info(f"已发送: {title}") | |
| return ok | |
| def chat_loop(config_path: Path, *, send_ding: bool = True) -> None: | |
| """本地交互模式:读取用户输入 → 分析 → 可选推送到钉钉群。""" | |
| print("=" * 50) | |
| print(" DM 广告数据分析 Agent(交互模式)") | |
| print("=" * 50) | |
| print() | |
| if send_ding: | |
| print("输入分析指令,报告将通过钉钉 Webhook 推送到群。") | |
| else: | |
| print("【调试模式】报告只打印在本终端,不会推送到钉钉。") | |
| print("支持的指令:") | |
| print(" 分析最新一周 / 分析上周") | |
| print(" 分析25年12月 / 分析2025年12月") | |
| print(" 分析W9 / 分析第9周 / 分析2025 W40") | |
| print(" exit / quit 退出") | |
| print() | |
| while True: | |
| try: | |
| user_input = input(">>> ").strip() | |
| except (EOFError, KeyboardInterrupt): | |
| print("\n再见!") | |
| break | |
| if not user_input: | |
| continue | |
| if user_input.lower() in ("exit", "quit", "q", "退出"): | |
| print("再见!") | |
| break | |
| if user_input in ("帮助", "help", "?", "?"): | |
| print(HELP_TEXT) | |
| continue | |
| params = parse_query(user_input) | |
| if params is None: | |
| print(f"无法识别「{user_input}」,请输入「帮助」查看支持的指令。") | |
| continue | |
| desc = describe_query(params) | |
| print(f"正在分析: {desc} ...") | |
| try: | |
| title, body = _do_analysis(params, config_path) | |
| except Exception as e: | |
| print(f"分析出错: {e}") | |
| continue | |
| print(f"报告已生成: {title}({len(body)} 字符)") | |
| if not send_ding: | |
| print("\n--- 报告正文(未推送钉钉)---\n") | |
| print(body) | |
| print("\n--- 结束 ---\n") | |
| continue | |
| ok, err = send_markdown(title=title, text=body) | |
| if ok: | |
| print(f"已推送到钉钉群。") | |
| else: | |
| print(f"钉钉推送失败: {err}") | |
| print("--- 报告内容 ---") | |
| print(body) | |
| print("---") | |
| print() | |
| def main() -> None: | |
| _configure_stdout_utf8() | |
| parser = argparse.ArgumentParser(description="广告数据周度分析 Agent") | |
| parser.add_argument("--web", action="store_true", help="启动网页界面(推荐)") | |
| parser.add_argument("--chat", action="store_true", help="终端交互模式") | |
| parser.add_argument("--run-once", action="store_true", help="分析最新一周并推送钉钉(加 --no-ding 则只打印到终端)") | |
| parser.add_argument("--no-ding", action="store_true", help="不调用钉钉,仅终端输出(配合 --run-once 或 --chat)") | |
| parser.add_argument("--config", type=Path, default=None, help="config.yaml 路径") | |
| parser.add_argument("--week", type=str, default=None, help="指定分析周 (YYYY-W)") | |
| parser.add_argument("--month", type=str, default=None, help="指定分析月 (YYYY-M)") | |
| parser.add_argument("--dry-run", action="store_true", help="只输出报告到终端,不推送钉钉") | |
| parser.add_argument("--preview", action="store_true", help="同 --dry-run,便于记") | |
| args = parser.parse_args() | |
| config_path = args.config or _root / "config.yaml" | |
| # ── 网页界面 ── | |
| if args.web: | |
| from src.web import start_web | |
| start_web() | |
| return | |
| # ── 终端交互模式 ── | |
| if args.chat: | |
| chat_loop(config_path, send_ding=not args.no_ding) | |
| return | |
| # ── 解析时间参数 ── | |
| target_year, target_week, target_month = None, None, None | |
| if args.week: | |
| parts = args.week.split("-") | |
| target_year, target_week = int(parts[0]), int(parts[1]) | |
| if args.month: | |
| parts = args.month.split("-") | |
| target_year, target_month = int(parts[0]), int(parts[1]) | |
| # ── Dry-run / Preview ── | |
| if args.dry_run or args.preview: | |
| if target_month is not None: | |
| result = run_monthly_analysis( | |
| config_path=config_path, | |
| target_year=target_year, | |
| target_month=target_month, | |
| ) | |
| title = format_monthly_title(result) | |
| text = format_monthly_report(result) | |
| else: | |
| result = run_analysis( | |
| config_path=config_path, | |
| target_year=target_year, | |
| target_week=target_week, | |
| ) | |
| title = format_title(result) | |
| text = format_report(result) | |
| print(f"=== {title} ===") | |
| print(text) | |
| return | |
| # ── Run-once ── | |
| if args.run_once: | |
| run_once( | |
| config_path=config_path, | |
| target_year=target_year, | |
| target_week=target_week, | |
| send_ding=not args.no_ding, | |
| ) | |
| return | |
| # ── 定时任务 ── | |
| from apscheduler.schedulers.blocking import BlockingScheduler | |
| from apscheduler.triggers.cron import CronTrigger | |
| config = load_config(config_path) | |
| cron_expr = (config.get("schedule", {}).get("weekly_cron") or "0 9 * * 1").strip() | |
| parts = cron_expr.split() | |
| if len(parts) != 5: | |
| minute, hour, day, month, dow = "0", "9", "*", "*", "1" | |
| else: | |
| minute, hour, day, month, dow = parts | |
| scheduler = BlockingScheduler() | |
| scheduler.add_job( | |
| run_once, | |
| CronTrigger(minute=minute, hour=hour, day=day, month=month, day_of_week=dow), | |
| args=[config_path], | |
| id="weekly_report", | |
| ) | |
| log.info(f"定时任务已启动,cron: {cron_expr}。按 Ctrl+C 退出。") | |
| scheduler.start() | |
| if __name__ == "__main__": | |
| main() | |