""" 广告智能决策引擎配置 — auto_put_ad_mini 运营可直接修改此文件调整决策参数。 当前模式:智能判断 - 基于 动态 ROI (7日均值) 的精细化决策 - AI 推理结合领域知识 - 三级分类:零消耗待关停(规则)+ 待优化评估(智能)+ 正常运行(规则) """ import os import logging from pathlib import Path from agent.core.runner import RunConfig, KnowledgeConfig # 初始化 logger(必须在使用前定义) logger = logging.getLogger(__name__) # 加载 .env 文件(如果存在) try: from dotenv import load_dotenv load_dotenv(Path(__file__).parent / ".env") except ImportError: pass # ═══════════════════════════════════════════ # Agent 运行配置 # ═══════════════════════════════════════════ MAIN_CONFIG = RunConfig( model="anthropic/claude-sonnet-4.5", temperature=0.3, max_iterations=50, name="广告智能调控助手", tools=[ "fetch_creative_data", "merge_creative_data", "calculate_roi_metrics", "calculate_portfolio_summary", "get_ads_for_review", "apply_decisions", "query_ad_detail", # Mode 2: 查询广告详情 "modify_decisions", # Mode 3: 修改已有决策 "validate_decisions", "generate_report", # 执行引擎 + IM 审批(已集成阻塞式审批流): "execute_decisions", "check_execution_feedback", "send_approval_request", "check_approval_status", "send_feishu_text_message", # 执行后向您同步 diff / 确认 / 质疑回应 # 飞书文档(报告导入 & 分享): "import_to_feishu", # 注:曾考虑用内置 "agent" 工具按 tier 并行委托子 Agent, # 但框架的 agent 工具只返回文本 summary,主 Agent 拿不回结构化决策, # 会陷入"无法 apply"的死循环。直接在主 Agent 单次输出完成全部 decisions 更可靠。 ], skills=[ "ad-domain", # 业务模型:裂变模型、R值、ROI公式、字段定义 "platform-rules", # 平台硬约束:oCPM学习期、调价上限、数据口径 "decision-strategy", # 决策策略:角色 + 基准 + 候选标记 + 年龄策略 + 7种action + 输出规范 "posterior-wisdom", # 后验经验:学习中断/降价恢复/创意冷启动/置信度分级 ], extra_llm_params={"max_tokens": 32000}, knowledge=KnowledgeConfig( enable_extraction=False, # 从决策过程中提取后验经验(投放后开启) enable_completion_extraction=False, # 完成后总结本轮经验(投放后开启) enable_injection=False, # 决策时自动注入相关历史经验(投放后开启) owner="ad_mini_team", ), ) SKILLS_DIR = str(Path(__file__).parent / "skills") TRACE_STORE_PATH = ".trace" LOG_LEVEL = "INFO" LOG_FILE = None # ═══════════════════════════════════════════ # 时区配置(海外部署) # ═══════════════════════════════════════════ TIMEZONE = os.getenv("TZ", "UTC") logger.info(f"运行时区:{TIMEZONE}") # ═══════════════════════════════════════════ # V3 数据窗口配置 # ═══════════════════════════════════════════ DATA_WINDOW_DAYS = 7 # 测试阶段:采集 7 天历史数据 ROI_CALCULATION_DAYS = 7 # 动态 ROI (7日均值) 计算窗口 # ═══════════════════════════════════════════ # V3 决策阈值(默认值,可被 SKILL 覆盖) # ═══════════════════════════════════════════ MIN_DAILY_COST = 100 # 日消耗 >= 100元才参与 ROI 计算 MIN_AD_AGE_DAYS = 3 # 广告创建 >= 3天才参与决策(与 min_periods 对齐) ROI_LOW_FACTOR = 0.75 # 动态 ROI (7日均值) < 全体均值 × 0.75 → 关停 NO_SPEND_THRESHOLD = 10 # 7日消耗均值 < 10元 → 关停 STABLE_SPEND_THRESHOLD = 100 # 稳定消耗定义:>100元/天 # ═══════════════════════════════════════════ # 出价调整配置 # ═══════════════════════════════════════════ BID_ADJUSTMENT_ENABLED = True BID_DOWN_ROI_FACTOR = 0.90 # ROI < 均值×0.90 → 考虑降价(低于渠道均值10%) BID_UP_ROI_FACTOR = 1.05 # ROI > 均值×1.05 → 考虑提价(高于渠道均值5%) BID_UP_MAX_SPEND = 1000 # 提价消耗上限:均值消耗<1000才提价(投手经验原文) BID_CHANGE_MIN_PCT = 0.03 # 最小调幅 3%(兼容旧代码) BID_CHANGE_MAX_PCT = 0.10 # 最大单次调幅 10%(兼容旧代码) BID_UP_MIN_PCT = 0.05 # 提价最小幅度 5% BID_UP_MAX_PCT = 0.10 # 提价最大幅度 10% BID_DOWN_MIN_PCT = 0.03 # 降价最小幅度 3% BID_DOWN_MAX_PCT = 0.05 # 降价最大幅度 5% BID_DOWN_MIN_SPEND = 500 # 降价消耗门槛:7日日均消耗≥500元 BID_FLOOR_YUAN = 0.05 # 出价下限(元) BID_CEILING_YUAN = 1.00 # 出价上限(元) # 广告年龄分段(基于决策树图片) COLD_START_DAYS = 3 # 冷启动期(≤3天):极度保护,几乎不干预 EARLY_GROWTH_DAYS = 7 # 早期成长期(4-7天):可提价放量(满足ROI+消耗条件) AD_AGE_MATURE = 7 # 成熟期(>7天):全面调控 # 兼容性(已废弃) AD_AGE_NEWBORN = COLD_START_DAYS # 兼容旧代码 CAUTIOUS_DAYS = EARLY_GROWTH_DAYS # 兼容旧代码 # 高燃烧预警配置 HIGH_BURN_AGE_THRESHOLD = 3 # 广告年龄>3天才检查 HIGH_BURN_COST_THRESHOLD = 300 # 昨日消耗>300元触发预警 ROI_LOW_MIN_YESTERDAY_COST = 300 # 关停消耗门槛:昨日消耗≥300才检查关停(投手经验2.4) # ═══════════════════════════════════════════ # 安全护栏配置 # ═══════════════════════════════════════════ GUARDRAILS_ENABLED = True DRY_RUN_MODE = False # 关闭干运行,让护栏正常放行(实际执行由 EXECUTION_ENABLED 控制) MAX_ADJUSTMENTS_PER_AD_PER_DAY = 2 MIN_ADJUSTMENT_INTERVAL_HOURS = 6 MAX_DAILY_CUMULATIVE_CHANGE_PCT = 0.20 # 日累计调幅上限 20% MAX_DAILY_OPS = 10000 # 单日最多操作广告数(实际不限制) DATA_FRESHNESS_MAX_HOURS = 96 # 数据超过 96 小时视为过期(已从48小时放宽至96小时) # ═══════════════════════════════════════════ # 执行引擎配置 # ═══════════════════════════════════════════ # 执行开关(优先级:数据库 > 环境变量 > 默认值False) EXECUTION_ENABLED = False try: from db import get_system_config _db_execution_enabled = get_system_config("execution_enabled", default=None) if _db_execution_enabled is not None: EXECUTION_ENABLED = _db_execution_enabled logger.info(f"✅ 从数据库读取执行开关:{EXECUTION_ENABLED}") else: # 降级到环境变量 _env_execution_enabled = os.getenv("EXECUTION_ENABLED", "").strip().lower() if _env_execution_enabled: EXECUTION_ENABLED = _env_execution_enabled in ("true", "1", "yes") logger.info(f"从环境变量读取执行开关:{EXECUTION_ENABLED}") except Exception as e: logger.warning(f"⚠️ 数据库读取执行开关失败({e}),使用默认值:{EXECUTION_ENABLED}") API_QPS_LIMIT = 8 # 保守QPS(平台上限10) API_MAX_RETRIES = 3 TIER1_MAX_CHANGE_PCT = 0.00 # Tier1自动执行已禁用(改为0%,所有操作都需审批) TIER3_MIN_DAILY_SPEND = 1500 # 高价值广告门槛(元/天) FEEDBACK_CHECK_HOURS = 6 # ═══════════════════════════════════════════ # IM 审批配置(飞书直连) # ═══════════════════════════════════════════ IM_ENABLED = True # IM 主开关(True 时审批消息发飞书) IM_APPROVAL_TIMEOUT_MINUTES = 30 # 审批超时(分钟) IM_APPROVAL_POLL_INTERVAL_SECONDS = 30 # 审批轮询间隔(秒) # 飞书应用凭据("增长投放"机器人)— 优先从环境变量读取 FEISHU_APP_ID = os.getenv("FEISHU_APP_ID", "cli_a955e97067f85cb3") FEISHU_APP_SECRET = os.getenv("FEISHU_APP_SECRET", "NQaG4ci1plXRDTgwCqrLJgMLLoA2tdF8") # 运营审批人飞书信息 FEISHU_OPERATOR_OPEN_ID = os.getenv("FEISHU_OPERATOR_OPEN_ID", "ou_498988d823b61ab89c9afe4310f85bb4") FEISHU_OPERATOR_CHAT_ID = os.getenv("FEISHU_OPERATOR_CHAT_ID", "oc_88e0a1970a7de02eb5ac225a8b0cedea") # 投放项目群聊 — 用于接收决策结果通知和审批回复 # 置空则不发送到群,仅发送到个人 FEISHU_AD_PROJECT_CHAT_ID = os.getenv("FEISHU_AD_PROJECT_CHAT_ID", "") # 腾讯广告默认账户(测试账户) TENCENT_AD_ACCOUNT_ID = int(os.getenv("TENCENT_AD_ACCOUNT_ID", "80769799")) # ═══════════════════════════════════════════ # 账户白名单配置 # ═══════════════════════════════════════════ # 白名单模式开关(优先级:数据库 > 环境变量) WHITELIST_ENABLED = None WHITELIST_ACCOUNTS = [] # 尝试从数据库读取配置 try: from db import get_whitelist_accounts, get_system_config # 读取白名单开关 WHITELIST_ENABLED = get_system_config("whitelist_enabled", default=None) # 读取白名单账户列表 WHITELIST_ACCOUNTS = get_whitelist_accounts() logger.info(f"✅ 从数据库读取白名单配置:{len(WHITELIST_ACCOUNTS)} 个账户") except Exception as db_error: logger.warning(f"⚠️ 数据库读取失败({db_error}),降级到环境变量配置") # 降级方案1:从环境变量读取 _whitelist_str = os.getenv("WHITELIST_ACCOUNTS", "") if _whitelist_str: # 格式:逗号分隔,如 "80769799,71305011" WHITELIST_ACCOUNTS = [int(x.strip()) for x in _whitelist_str.split(",") if x.strip()] logger.info(f"从环境变量读取白名单:{len(WHITELIST_ACCOUNTS)} 个账户") else: # 降级方案2:从文件读取(可选) _whitelist_file = Path(__file__).parent / "whitelist.json" if _whitelist_file.exists(): import json with open(_whitelist_file) as f: whitelist_data = json.load(f) WHITELIST_ACCOUNTS = whitelist_data.get("accounts", []) logger.info(f"从 whitelist.json 读取白名单:{len(WHITELIST_ACCOUNTS)} 个账户") # 白名单开关降级处理 if WHITELIST_ENABLED is None: WHITELIST_ENABLED = os.getenv("WHITELIST_ENABLED", "true").lower() == "true" # 向后兼容:单账户模式 if not WHITELIST_ACCOUNTS: WHITELIST_ACCOUNTS = [TENCENT_AD_ACCOUNT_ID] logger.info(f"白名单为空,使用单账户模式:{TENCENT_AD_ACCOUNT_ID}") logger.info( f"白名单配置:{'启用' if WHITELIST_ENABLED else '禁用'}," f"账户数={len(WHITELIST_ACCOUNTS)},列表={WHITELIST_ACCOUNTS[:5]}..." ) # ═══════════════════════════════════════════ # 输出路径配置 # ═══════════════════════════════════════════ OUTPUTS_DIR = Path(__file__).parent / "outputs" RAW_DATA_DIR = OUTPUTS_DIR / "raw" # 创意级原始 CSV AD_STATUS_DIR = OUTPUTS_DIR / "ad_status" # 广告状态 CSV REPORTS_DIR = OUTPUTS_DIR / "reports" # 决策报告 EXECUTION_LOG_DIR = OUTPUTS_DIR / "execution_log" # 执行审计日志 DATA_DIR = OUTPUTS_DIR / "data" # 运行时数据(如调整历史) ADJUSTMENT_HISTORY_PATH = DATA_DIR / "adjustment_history.json" # ═══════════════════════════════════════════ # 人群包系数(保留,用于展示) # ═══════════════════════════════════════════ AUDIENCE_COEFFICIENTS = { "R500": 3.0, "R330+": 2.5, "R330": 2.0, "R180": 1.5, "R100": 1.2, "R50": 1.0, "R10": 1.0, "R2": 1.0, "default": 1.0, } # 从广告名称提取 R 值的匹配顺序 AUDIENCE_TIER_PATTERNS = [ ("R500", ["R500", "R_500", "r500"]), ("R330+", ["回流330+", "回流330+-", "回流q330", "330+全品类", "R330+", "R_330+"]), ("R330", ["回流330", "R330", "R_330", "定向330", "r330", "r300"]), ("R180", ["回流180", "R180", "R_180", "定向180", "r180", "r180-330", "r180-300", "R100-180", "R_100-180", "r100-180"]), ("R100", ["回流100", "R100", "R_100", "定向100", "r100", "R50-100"]), ("R50", ["回流50", "R50", "R_50", "r50"]), ("R10", ["R_10", "R10", "r10"]), ("R2", ["R_2", "R2", "r2"]), ]