Sfoglia il codice sorgente

fix(auto_put_ad_mini): 零消耗逻辑排除已关停广告 + 过期文档清理

- 零消耗判断跳过 SUSPEND/DELETED 状态广告,避免1047条已关停广告重复进入决策
- 删除 ARCHITECTURE.md(含过期"×0.5"旧公式,已被 skills/ 完整取代)
- ad_domain R值表精简、platform_rules 移除不准确的归因窗口描述
- 测试脚本适配 tier_batches 分批结构

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
刘立冬 3 settimane fa
parent
commit
b374418968

+ 0 - 1139
examples/auto_put_ad_mini/ARCHITECTURE.md

@@ -1,1139 +0,0 @@
-# auto_put_ad_mini — 智能投放 Agent 架构全解析
-
-> 基于 Reson Agent 框架的腾讯广告智能调控系统
-> 数据驱动 + AI推理 + 安全护栏 + 自然语言审批
-
----
-
-## 📐 架构总览
-
-```
-┌─────────────────────────────────────────────────────────────────┐
-│                      用户/运营人员                                │
-│          "分析广告" | "广告XXX降价10%" | "不要暂停"               │
-└───────────────────────────────┬─────────────────────────────────┘
-                                │
-                     ┌──────────▼──────────┐
-                     │   Agent运行引擎      │
-                     │  (AgentRunner)      │
-                     │                     │
-                     │  • LLM调用 (qwen)   │
-                     │  • 工具注册/调用     │
-                     │  • Skill加载        │
-                     │  • 轨迹追踪         │
-                     └──────────┬──────────┘
-                                │
-          ┌─────────────────────┼─────────────────────┐
-          │                     │                     │
-  ┌───────▼────────┐   ┌────────▼────────┐   ┌───────▼────────┐
-  │  system.prompt │   │   Skills/知识库  │   │   Tools/工具集  │
-  │  ─────────────│   │  ─────────────  │   │  ───────────── │
-  │  • Mode路由    │   │ roi-strategy.md │   │ data_query.py  │
-  │  • 决策流程    │   │ guardrail_rules │   │ roi_calculator │
-  │  • 审批逻辑    │   │ ad_domain.md    │   │ ad_decision    │
-  │  • 错误处理    │   │  (领域知识注入)  │   │ guardrails     │
-  └────────────────┘   └─────────────────┘   │ execution_engine│
-                                              │ im_approval    │
-                                              │ report_generator│
-                                              └────────────────┘
-                                                      │
-                     ┌────────────────────────────────┼────────────┐
-                     │                                │            │
-            ┌────────▼─────────┐         ┌───────────▼──────────┐ │
-            │   数据层 (ODPS)   │         │   外部服务集成        │ │
-            │  ─────────────   │         │  ──────────────      │ │
-            │  • 创意数据       │         │  • 飞书机器人        │ │
-            │  • 广告状态       │         │  • 腾讯广告 API      │ │
-            │  • Merged数据     │         │  (分级执行+回调)     │ │
-            └──────────────────┘         └─────────────────────┘ │
-                                                                  │
-                     ┌────────────────────────────────────────────┘
-                     │
-            ┌────────▼────────┐
-            │   持久化存储     │
-            │  ─────────────  │
-            │  • metrics_*.csv│
-            │  • decisions_*  │
-            │  • reports_*    │
-            │  • 调整历史记录  │
-            └─────────────────┘
-```
-
----
-
-## 🔄 完整执行流程详解
-
-### Mode 1: 全量分析工作流(主流程)
-
-用户说 **"分析广告"** 时触发,执行以下步骤:
-
-#### 第1步:数据拉取 (`fetch_creative_data`)
-```python
-# 工具: examples/auto_put_ad_mini/tools/data_query.py
-
-输入: days=7 (拉取最近7天)
-处理:
-  1. 检查本地已有数据(跳过重复拉取)
-  2. 对于缺失日期,从ODPS拉取创意级数据
-  3. 同时拉取广告状态数据 (ad_status)
-
-输出:
-  - outputs/raw/creative_YYYYMMDD.csv (每日创意数据)
-  - outputs/ad_status/ad_status_YYYYMMDD.csv (每日广告状态)
-
-数据特点:
-  • 创意级粒度 (一个广告有多个创意)
-  • 包含ROI计算必需字段: cost, open_count, fission0_count, total_revenue, etc.
-```
-
-#### 第2步:数据合并 (`merge_creative_data`)
-```python
-# 将创意数据 + 广告状态 合并
-
-输入: days=7
-处理:
-  1. 读取 creative_YYYYMMDD.csv
-  2. 读取 ad_status_YYYYMMDD.csv
-  3. LEFT JOIN (创意 + 广告状态)
-
-输出:
-  - outputs/merged/merged_YYYYMMDD.csv
-
-字段包括:
-  • ad_id, creative_id, account_id, ad_name
-  • cost, view_count, click_count
-  • open_count, fission0_count, total_return_count, total_revenue
-  • bid_amount, configured_status, create_time
-```
-
-#### 第3步:ROI计算 (`calculate_roi_metrics`)
-```python
-# 工具: examples/auto_put_ad_mini/tools/roi_calculator.py
-
-输入: end_date="20260415" (默认yesterday)
-处理:
-  1. 加载最近30天的 merged 数据(容错缺失)
-  2. 聚合到广告级 (GROUP BY ad_id, date)
-  3. 计算 动态 ROI (7日均值) (考虑裂变效率稳定性)
-  4. 计算 7日汇总 (cost_7d_avg, revenue_7d_total)
-  5. 计算 30日汇总 (stable_spend_days_30d)
-
-核心公式:
-  T0裂变系数 = fission0_count / open_count
-  arpu = total_revenue / total_return_count
-  当日裂变收益率 = fission0_count * arpu / cost
-  当日回流倍数 = total_return_count / open_count
-
-  裂变效率稳定因子 = 回流倍数_7日均值 / T0裂变系数_7日均值
-  动态 ROI (7日均值) = 当日裂变收益率 * 裂变效率稳定因子
-
-输出:
-  - outputs/metrics_20260415.csv (1570行, 每个广告一行)
-
-关键指标:
-  • 动态ROI (单日值)
-  • 动态ROI_7日均值 (决策参考值!)
-  • cost_7d_avg, cost_7d_total
-  • ad_age_days (广告年龄)
-```
-
-#### 第4步:广告分类 (`get_ads_for_review`)
-```python
-# 工具: examples/auto_put_ad_mini/tools/ad_decision.py
-
-输入: metrics_csv (步骤3输出)
-处理:
-  1. 计算全体 ROI 分布 (mean, p25, p50, p75, p90)
-  2. 检测衰退信号 (提价、换创意、消耗下降)
-  3. 分为三类(语义化命名):
-
-     【零消耗待关停】(极端差, 自动关停):
-       • 7日均消耗 < 1元 (几乎零活动)
-
-     【待评估(候选)】(边缘, 需AI推理):
-       • ROI < 全体均值 × 0.8 (偏低)
-       • 或检测到衰退信号
-
-     【正常运行】(自动保持):
-       • 其余广告
-
-输出: JSON结构化数据
-  {
-    "summary": {total, zero_spend_ads, need_review_ads, normal_ads},
-    "distribution": {channel_roi_p50, p25, p50, p75, p90},
-    "bid_adjustment": {bid_down_line, bid_up_line},
-    "zero_spend_ads": [...],  # 自动关停列表
-    "need_review_ads": [...], # 需要推理的广告(含详细指标)
-    "normal_ads_summary": {...}
-  }
-```
-
-#### 第5步:AI推理决策 (Agent自主推理)
-```python
-# Agent读取【待评估(候选)】广告数据 + roi-strategy.md技能
-# 对每个广告进行推理,输出决策JSON
-
-决策逻辑框架:
-  IF ad_age_days < 4天:
-    → hold (冷启动绝对保护)
-
-  ELIF ad_age_days < 7天:
-    → 仅允许小幅降价 ≤5% (谨慎期)
-
-  ELIF ROI < 全体均值 × 0.5:
-    → pause (极低ROI, 关停)
-
-  ELIF ROI < 全体均值 × 0.8 AND cost_7d_avg ≥ 100元:
-    → bid_down -3%~-10% (降价, 越接近关停线降幅越大)
-
-  ELIF ROI > 全体均值 × 1.2 AND cost_7d_avg < 中位数 × 0.5:
-    → bid_up +3%~-10% (高ROI低消耗, 提价放量)
-
-  ELSE:
-    → hold (保持)
-
-输出格式:
-  [
-    {
-      "ad_id": 90289631207,
-      "action": "pause",
-      "dimension": "ROI过低",
-      "reason": "动态 ROI (7日均值)=1.18 < 均值3.29×0.5=1.64, 日消耗1524元, 持续亏损",
-      "confidence": "high"
-    },
-    {
-      "ad_id": 32912382309,
-      "action": "bid_down",
-      "dimension": "ROI偏低-降价",
-      "reason": "动态ROI_7日均值=1.81 < 均值3.29×0.8=2.63, 建议降8%至0.30元",
-      "confidence": "medium",
-      "recommended_change_pct": -0.08
-    },
-    ...
-  ]
-```
-
-#### 第6步:保存决策 (`apply_decisions`)
-```python
-# 工具: examples/auto_put_ad_mini/tools/ad_decision.py
-
-输入: decisions (AI推理的JSON) + metrics_csv
-处理:
-  1. 解析AI输出的JSON
-  2. 合并【零消耗待关停】自动关停 + 【待评估】AI决策 + 【正常运行】自动保持
-  3. 过滤已暂停广告 (AD_STATUS_SUSPEND)
-  4. 计算出价变更(current_bid × (1 + recommended_change_pct))
-
-输出:
-  - outputs/reports/llm_decisions_20260415.csv
-
-字段:
-  ad_id, action, dimension, reason, confidence,
-  recommended_change_pct, current_bid, recommended_bid,
-  ad_age_days
-```
-
-#### 第7步:安全护栏验证 (`validate_decisions`)
-```python
-# 工具: examples/auto_put_ad_mini/tools/guardrails.py
-
-输入: llm_decisions_20260415.csv
-处理: 逐条检查6道护栏
-
-护栏1: 数据新鲜度
-  IF 数据年龄 > 48小时:
-    → Block所有非hold操作
-
-护栏2: 冷启动保护
-  IF ad_age_days < 4天:
-    → Block所有pause/bid_down
-  IF ad_age_days < 7天:
-    → bid_down限制 ≤5%
-
-护栏3: 出价边界
-  IF recommended_bid < 0.5元:
-    → Modified (钳位到0.5元)
-  IF recommended_bid > 200元:
-    → Modified (钳位到200元)
-
-护栏4: 频率限制
-  IF 今日已调整次数 ≥ 2:
-    → Block
-  IF 距上次调整 < 6小时:
-    → Block
-  IF 今日累计调幅 > 20%:
-    → Block
-
-护栏5: 每日操作上限
-  IF 今日已操作广告数 ≥ 200:
-    → 按ROI严重度排序, Block低优先级
-
-护栏6: 干运行模式
-  IF DRY_RUN_MODE = True:
-    → 所有操作标记dry_run (Modified)
-
-输出:
-  - outputs/reports/validated_decisions_20260415.csv
-
-新增字段:
-  guardrail_status: approved | modified | blocked
-  guardrail_reason: 拦截原因说明
-  final_action: 护栏处理后的最终动作
-  final_bid: 护栏处理后的最终出价
-```
-
-#### 第8步:飞书审批请求 (`send_approval_request`)
-```python
-# 工具: examples/auto_put_ad_mini/tools/im_approval.py
-# ⚠️ 仅当有通过护栏的非hold操作时才调用
-
-输入: wait_for_reply=True (阻塞等待审批)
-处理:
-  1. 读取 validated_decisions (guardrail_status=approved)
-  2. 按风险分级:
-     Tier 1 (≤5%调幅): 仅通知, 不需审批
-     Tier 2 (pause, bid_down>5%): 需审批
-     Tier 3 (日消耗>1500元): 强制审批
-
-  3. 构造飞书消息 (卡片式, 带数据表格)
-  4. 发送到飞书群组 (FEISHU_OPERATOR_CHAT_ID)
-  5. 阻塞等待运营回复 (轮询检查, 超时30分钟)
-
-飞书消息格式:
-  【广告调控审批】2026-04-15
-
-  📊 决策摘要
-  • 总决策数: 38个
-  • Tier 1 (自动): 10个 (小幅调价 ≤5%)
-  • Tier 2 (需审批): 20个 (暂停/降价>5%)
-  • Tier 3 (高价值): 8个 (日消耗>1500元)
-
-  🔻 高风险广告 (需审批)
-  1. 广告90289631207: ROI=1.18, 消耗=1524元/天 → 暂停
-  2. 广告37429627354: ROI=2.37, 消耗=1228元/天 → 降价5%
-  ...
-
-  ⏰ 请在30分钟内回复:
-  - "批准" / "通过" → 全部批准
-  - "拒绝" / "取消" → 全部拒绝
-  - "广告XXX不要暂停" → 修改特定决策
-
-审批结果:
-  - approved_decisions: 通过的决策列表
-  - rejected_decisions: 拒绝的决策列表
-  - modified_decisions: 修改后的决策列表
-```
-
-**自然语言审批理解 (关键特性):**
-```python
-# Agent具备自然语言理解能力, 可以处理灵活的人类指令
-
-运营说: "批准"
-  → 解析为: 全部批准
-
-运营说: "广告90289631207不要暂停, 改为降价10%"
-  → 解析为: modify_decisions([
-      {"ad_id": 90289631207, "new_action": "bid_down", "new_change_pct": -0.10}
-    ])
-  → 重新validate → 重新发审批
-
-运营说: "只批准降价的, 暂停的全部拒绝"
-  → 解析为: 过滤 action=bid_down → approved
-              过滤 action=pause → rejected
-
-运营说: "为什么要暂停广告90289631207?"
-  → Agent回答: "该广告动态 ROI (7日均值)=1.18 < 关停线1.64,日消耗1524元,
-                已持续亏损,建议暂停止损"
-  → 等待运营最终确认
-```
-
-#### 第9步:执行决策 (`execute_decisions`)
-```python
-# 工具: examples/auto_put_ad_mini/tools/execution_engine.py
-# ⚠️ 仅在运营审批通过后调用
-
-输入: 运营审批通过的决策列表
-处理:
-  1. 分批执行 (QPS限制=8, 批次大小=50)
-  2. 根据action调用腾讯广告API:
-     • pause → /v3.0/adgroups/update (configured_status=SUSPEND)
-     • bid_down/bid_up → /v3.0/adgroups/update (bid_amount=final_bid)
-
-  3. 错误处理 + 重试 (最多3次)
-  4. 记录执行日志
-
-输出:
-  - outputs/execution_log/execution_20260415.json
-
-执行结果:
-  {
-    "timestamp": "2026-04-15 14:30:00",
-    "total": 38,
-    "success": 36,
-    "failed": 2,
-    "details": [
-      {
-        "ad_id": 90289631207,
-        "action": "pause",
-        "status": "success",
-        "api_response": {...}
-      },
-      {
-        "ad_id": 32912382309,
-        "action": "bid_down",
-        "status": "failed",
-        "error": "API限流, 重试后成功"
-      }
-    ]
-  }
-```
-
-#### 第10步:生成报告 (`generate_report`)
-```python
-# 工具: examples/auto_put_ad_mini/tools/report_generator.py
-
-输入: validated_decisions_20260415.csv
-处理:
-  1. 汇总统计 (总数, pause/bid_down/hold分布)
-  2. 按ROI严重度排序
-  3. 生成Excel (带条件格式、颜色标注)
-
-输出:
-  - outputs/reports/decision_20260415.csv (纯数据)
-  - outputs/reports/decision_20260415.xlsx (带格式)
-
-Excel样式:
-  • 绿色: ROI优秀 (>均值×1.2)
-  • 黄色: ROI偏低 (均值×0.5~0.8)
-  • 红色: ROI极低 (<均值×0.5)
-  • 冻结首行, 自动筛选
-```
-
----
-
-### Mode 2: 定向操作工作流
-
-用户提及 **具体广告ID + 操作意图** 时触发(如 "广告90289631207降价10%"):
-
-```
-1. query_ad_detail(ad_id) → 查询当前数据+全局上下文
-2. AI推理 → 根据用户意图+当前状态生成决策JSON
-3. modify_decisions() / apply_decisions() → 保存决策 (upsert模式)
-4. validate_decisions() → 护栏验证
-5. send_approval_request() → IM发给运营确认
-6. execute_decisions() → 执行
-```
-
----
-
-### Mode 3: 反馈修改工作流
-
-用户对已有决策提出修改意见时触发(如 "广告XXX不要暂停"):
-
-```
-1. modify_decisions(modifications) → 修改指定条目
-   支持:
-     • 精确修改: [{"ad_id": "XXX", "new_action": "bid_down", "new_change_pct": -0.05}]
-     • 批量修改: [{"filter": "all_pause", "new_action": "hold"}]
-
-2. validate_decisions() → 重新验证
-3. send_approval_request() → 重新发IM
-4. execute_decisions() → 执行
-```
-
----
-
-## 🛡️ 安全护栏系统详解
-
-护栏是整个系统的**核心安全机制**,防止AI做出错误决策。
-
-### 护栏1: 数据新鲜度检查
-```python
-规则:
-  IF (当前时间 - 数据时间) > 48小时:
-    → Block所有非hold操作
-
-原因:
-  • 广告数据实时性强, 过期数据会导致错误决策
-  • 超过48小时的数据已失去参考价值
-
-示例:
-  数据: 20260415 (58小时前)
-  当前: 20260417 10:26
-  → Block所有pause/bid_down, 转为hold
-```
-
-### 护栏2: 冷启动保护
-```python
-规则:
-  IF ad_age_days < 4天:
-    → Block所有负向操作 (pause, bid_down)
-
-  IF 4天 ≤ ad_age_days < 7天:
-    → pause仍然Block
-    → bid_down限制最大降幅5%
-
-原因:
-  • 新广告需要时间学习用户画像
-  • 初始出价通常比目标CPA高20%, 需等系统优化
-  • 过早干预会打断学习过程
-
-示例:
-  广告90289631207: ad_age_days=5天, AI建议pause
-  → Block (谨慎期不允许暂停)
-  → 转为hold
-```
-
-### 护栏3: 出价边界
-```python
-规则:
-  IF recommended_bid < 0.5元:
-    → Modified (钳位到0.5元)
-
-  IF recommended_bid > 200元:
-    → Modified (钳位到200元)
-
-原因:
-  • 低于0.5元竞争力不足, 无法获得曝光
-  • 高于200元风险过高, 可能是计算错误
-
-示例:
-  AI建议: bid_down -50%, current_bid=0.8元 → recommended_bid=0.4元
-  → Modified: final_bid=0.5元
-```
-
-### 护栏4: 频率限制
-```python
-规则:
-  IF 今日已对该广告调整次数 ≥ 2:
-    → Block
-
-  IF 距上次调整时间 < 6小时:
-    → Block
-
-  IF 今日累计调幅 > 20%:
-    → Block
-
-原因:
-  • 频繁调整会触发平台模型重学习
-  • 超过10%单次调幅会导致流量崩塌
-  • 需要给系统足够时间反馈效果
-
-示例:
-  广告32912382309: 今日已降价1次 (-5%), 距离上次调整3小时
-  AI再次建议降价 (-3%)
-  → Block (间隔不足6小时)
-```
-
-### 护栏5: 每日操作上限
-```python
-规则:
-  IF 今日已操作广告数 ≥ 200:
-    → 按ROI严重度排序
-    → Block低优先级广告
-
-原因:
-  • 避免一次性大规模调整
-  • 分散风险, 逐步优化
-
-优先级:
-  1. pause (极低ROI, 持续亏损)
-  2. bid_down (ROI接近关停线)
-  3. bid_up (高ROI低消耗)
-```
-
-### 护栏6: 干运行模式
-```python
-规则:
-  IF DRY_RUN_MODE = True:
-    → 所有操作标记dry_run
-    → 不实际调用API
-
-用途:
-  • 测试阶段验证决策逻辑
-  • 模拟执行, 查看效果预测
-```
-
----
-
-## 🔌 外部服务集成
-
-### 1. 腾讯广告 API v3.0
-
-**Token管理 (动态获取):**
-```python
-# tools/ad_api.py
-
-def _get_access_token(account_id):
-    """
-    优先从内部API获取 (30分钟缓存)
-    失败时降级使用 .env 静态token
-    """
-    url = f"https://api.piaoquantv.com/ad/put/tencent/getAccessToken?accountId={account_id}"
-    response = requests.get(url)
-    if response.ok:
-        return response.json()["data"]["accessToken"]
-    else:
-        return os.getenv("TENCENT_AD_ACCESS_TOKEN")
-```
-
-**广告操作API:**
-```python
-# 暂停广告
-POST /v3.0/adgroups/update
-{
-  "account_id": 80769799,
-  "adgroup_id": 90289631207,
-  "configured_status": "AD_STATUS_SUSPEND"
-}
-
-# 修改出价
-POST /v3.0/adgroups/update
-{
-  "account_id": 80769799,
-  "adgroup_id": 32912382309,
-  "bid_amount": 30  # 单位: 分 (0.30元)
-}
-```
-
-### 2. 飞书机器人
-
-**应用信息:**
-```python
-APP_ID = "cli_a955e97067f85cb3"
-APP_SECRET = "NQaG4ci1plXRDTgwCqrLJgMLLoA2tdF8"
-OPERATOR_OPEN_ID = "ou_498988d823b61ab89c9afe4310f85bb4"
-CHAT_ID = "oc_88e0a1970a7de02eb5ac225a8b0cedea"
-```
-
-**消息发送:**
-```python
-# 卡片式消息
-POST https://open.feishu.cn/open-api/im/v1/messages
-{
-  "receive_id": CHAT_ID,
-  "msg_type": "interactive",
-  "content": {
-    "config": {"wide_screen_mode": true},
-    "header": {
-      "title": {"tag": "plain_text", "content": "【广告调控审批】"}
-    },
-    "elements": [
-      {"tag": "markdown", "content": "**决策摘要**\n..."},
-      {"tag": "hr"},
-      {"tag": "action", "actions": [
-        {"tag": "button", "text": "批准", "type": "primary"},
-        {"tag": "button", "text": "拒绝", "type": "danger"}
-      ]}
-    ]
-  }
-}
-```
-
-**审批轮询:**
-```python
-# 每30秒检查一次运营回复
-while not timeout:
-    messages = get_chat_history(CHAT_ID)
-    for msg in messages:
-        if msg.sender == OPERATOR_OPEN_ID:
-            # 自然语言理解运营意图
-            intent = parse_approval_intent(msg.content)
-            if intent.type == "approve":
-                return ApprovalResult(approved=True)
-            elif intent.type == "modify":
-                return ApprovalResult(modified=intent.modifications)
-```
-
-### 3. ODPS (数据源)
-
-**数据拉取:**
-```python
-# tools/data_query.py
-
-def _fetch_from_odps(bizdate, account_id):
-    """
-    从MaxCompute (ODPS)拉取创意数据
-    """
-    sql = f"""
-    SELECT
-        bizdate,
-        ad_id,
-        creative_id,
-        ad_name,
-        cost,
-        open_count,
-        fission0_count,
-        total_return_count,
-        total_revenue,
-        view_count,
-        valid_click_count,
-        conversions_count
-    FROM creative_data_table
-    WHERE bizdate = {bizdate}
-      AND account_id = {account_id}
-    """
-
-    odps_client = ODPSClient(project="loghubods")
-    df = odps_client.query_to_dataframe(sql)
-    return df
-```
-
----
-
-## 📊 数据流转全景
-
-```
-[ODPS数据仓库]
-     ↓
- creative_data (创意级, 每日2.5MB)
-     ↓
-[data_query.py] fetch_creative_data
-     ↓
-outputs/raw/creative_YYYYMMDD.csv (原始创意数据)
-     ↓
-[data_query.py] merge_creative_data
-     ↓
-outputs/merged/merged_YYYYMMDD.csv (创意+广告状态合并)
-     ↓
-[roi_calculator.py] calculate_roi_metrics
-  • 加载最近30天 merged 数据
-  • 聚合到广告级 (GROUP BY ad_id, date)
-  • 计算 动态 ROI (7日均值)
-  • 计算 7日/30日汇总指标
-     ↓
-outputs/metrics_20260415.csv (1570行广告级指标)
-     ↓
-[ad_decision.py] get_ads_for_review
-  • 计算全体ROI分布
-  • 检测衰退信号
-  • 分类(零消耗待关停 / 待评估 / 正常运行)
-     ↓
-JSON结构化数据 (发给AI)
-     ↓
-[Agent AI推理] 52个【待评估(候选)】广告逐个分析
-     ↓
-decisions JSON (AI输出)
-     ↓
-[ad_decision.py] apply_decisions
-  • 合并三类决策(零消耗关停 + 待评估AI + 正常保持)
-  • 过滤已暂停广告
-  • 计算出价变更
-     ↓
-outputs/reports/llm_decisions_20260415.csv (613条决策)
-     ↓
-[guardrails.py] validate_decisions
-  • 6道护栏逐条检查
-  • 拦截/修正/通过
-     ↓
-outputs/reports/validated_decisions_20260415.csv (带护栏状态)
-     ↓
-[im_approval.py] send_approval_request
-  • 过滤 approved 决策
-  • 分级 (Tier 1/2/3)
-  • 发送飞书卡片
-  • 阻塞等待审批
-     ↓
-[运营审批] 自然语言回复
-     ↓
-[execution_engine.py] execute_decisions
-  • 调用腾讯广告API
-  • 记录执行日志
-     ↓
-outputs/execution_log/execution_20260415.json (执行结果)
-     ↓
-[report_generator.py] generate_report
-  • 汇总统计
-  • Excel美化
-     ↓
-outputs/reports/decision_20260415.xlsx (最终报告)
-```
-
----
-
-## 🎯 关键设计亮点
-
-### 1. 智能引擎 vs 规则引擎
-```python
-# config.py
-USE_RULE_ENGINE = False  # 规则引擎 (固定阈值, 快速)
-USE_AI_ENGINE = True     # 智能引擎 (AI推理, 灵活)
-
-优劣对比:
-  规则引擎:
-    ✅ 速度快 (秒级)
-    ✅ 可解释性强
-    ❌ 无法处理复杂场景
-    ❌ 阈值需人工调整
-
-  智能引擎:
-    ✅ 自适应 (动态阈值)
-    ✅ 处理复杂因果关系 (ROI+消耗+趋势)
-    ✅ 自然语言交互
-    ❌ 速度较慢 (分钟级)
-    ❌ 需要LLM调用成本
-```
-
-### 2. 分级执行策略
-```python
-# 风险分层审批, 平衡效率与安全
-
-Tier 1 (自动执行, 无需审批):
-  • 出价调整 ≤ 5%
-  • 日消耗 < 500元
-  → 实时生效, 仅通知运营
-
-Tier 2 (需审批):
-  • pause
-  • bid_down > 5%
-  • bid_up > 5%
-  → 发飞书等待审批
-
-Tier 3 (强制审批):
-  • 日消耗 > 1500元 (高价值广告)
-  • 出价调整 > 10% (高风险操作)
-  → 运营必须回复
-```
-
-### 3. 自然语言审批
-```python
-# 打破传统"批准/拒绝"二元模式
-# Agent理解运营的自然语言指令, 灵活调整
-
-示例1:
-  运营: "广告90289631207改为降价5%, 不要暂停"
-  → modify_decisions([{
-      "ad_id": 90289631207,
-      "new_action": "bid_down",
-      "new_change_pct": -0.05
-    }])
-  → validate → 重新发审批
-
-示例2:
-  运营: "ROI低于1.5的全部暂停, 其他批准"
-  → filter decisions where ROI < 1.5 → pause
-  → filter decisions where ROI >= 1.5 → approved
-  → execute
-
-示例3:
-  运营: "这个广告为什么要暂停? 我觉得还有机会"
-  → Agent解释: "该广告ROI=1.18, 低于关停线1.64, 已持续23天消耗
-                 1524元/天, 总亏损>1万元, 建议暂停止损"
-  → 运营: "那降价20%试试"
-  → Agent: "降价20%超过单次调幅上限10%, 已调整为-10%"
-  → modify_decisions → 重新验证 → 执行
-```
-
-### 4. 闭环反馈机制
-```python
-# 执行后6小时检查效果, 持续优化
-
-[execution_engine.py] execute_decisions
-  → 记录执行时间戳
-     ↓
-[execution_engine.py] check_execution_feedback (6小时后)
-  → 拉取最新数据
-  → 对比执行前后ROI变化
-  → 计算决策准确率
-     ↓
-决策准确率统计:
-  • pause正确率: 95% (暂停后ROI无改善)
-  • bid_down正确率: 80% (降价后ROI提升)
-  • bid_up正确率: 70% (提价后收入增长)
-     ↓
-反馈到下次决策:
-  • 调整阈值 (如ROI关停线从0.5→0.6)
-  • 优化调幅策略 (如降价步长从-8%→-10%)
-```
-
-### 5. 容错与降级
-```python
-# 多层级容错机制
-
-数据层:
-  • ODPS拉取失败 → 使用本地缓存
-  • merged数据缺失 → 跳过缺失日期, 使用可用数据
-
-API层:
-  • 腾讯广告API限流 → 自动重试 (指数退避)
-  • Token过期 → 自动刷新
-
-审批层:
-  • 飞书审批超时 (30分钟) → 自动取消, 保留决策供下次执行
-  • 网络错误 → 降级为邮件审批
-```
-
----
-
-## 📈 性能优化
-
-### 1. 数据加载优化
-```python
-# 避免重复拉取数据
-
-def fetch_creative_data(days=7):
-    for i in range(days):
-        date = (today - timedelta(days=i)).strftime("%Y%m%d")
-        csv_path = RAW_DIR / f"creative_{date}.csv"
-
-        if csv_path.exists() and csv_path.stat().st_size > 1000:
-            logger.info(f"跳过已有数据: {date}")
-            continue  # 跳过已存在的数据
-
-        # 仅拉取缺失数据
-        df = _fetch_from_odps(date, account_id)
-        df.to_csv(csv_path)
-```
-
-### 2. ROI计算缓存
-```python
-# 增量计算, 避免重复处理
-
-metrics_cache = {}
-
-def calculate_roi_metrics(end_date):
-    cache_key = end_date
-    if cache_key in metrics_cache:
-        return metrics_cache[cache_key]
-
-    # 计算新数据
-    result = _do_calculation(end_date)
-    metrics_cache[cache_key] = result
-    return result
-```
-
-### 3. API批量调用
-```python
-# 减少网络往返
-
-# ❌ 逐个调用 (慢)
-for decision in decisions:
-    update_ad(decision.ad_id, decision.final_bid)
-
-# ✅ 批量调用 (快)
-batch_update_ads([
-    {"adgroup_id": d.ad_id, "bid_amount": d.final_bid}
-    for d in decisions
-])
-```
-
----
-
-## 🚀 未来扩展方向
-
-### 1. 接入完整 auto_put_ad 体系
-```
-auto_put_ad_mini (当前)          →          auto_put_ad (完整版)
-┌───────────────────┐             ┌─────────────────────────────┐
-│ 监控调控 Agent    │      接入    │ 受众策略 Agent               │
-│  • ROI分析       │    ───────→  │ 创意策略 Agent               │
-│  • 出价调整      │              │ 预算策略 Agent               │
-│  • 广告暂停      │              │ ★ 监控调控 Agent (mini升级) │
-└───────────────────┘              │ 数据分析 Agent               │
-                                   │ 系统运维 Agent               │
-                                   │ 自学习/反馈环               │
-                                   └─────────────────────────────┘
-```
-
-### 2. 机器学习增强决策
-```python
-# 训练预测模型
-
-from sklearn.ensemble import RandomForestRegressor
-
-# 特征工程
-features = [
-    "cost_7d_avg", "roi_7d_avg", "ad_age_days",
-    "bid_amount", "audience_tier", "creative_count",
-    "cost_trend", "roi_trend"
-]
-
-# 训练目标: 预测未来7天ROI
-model = RandomForestRegressor()
-model.fit(X_train[features], y_train["future_7d_roi"])
-
-# 决策辅助
-future_roi_if_bid_down = model.predict(current_features + [-0.10])
-if future_roi_if_bid_down > current_roi:
-    recommend("bid_down", -0.10)
-```
-
-### 3. A/B测试框架
-```python
-# 对比不同策略效果
-
-def ab_test_bid_strategy():
-    """
-    将广告随机分为A/B组
-    A组: 激进策略 (降幅10%)
-    B组: 保守策略 (降幅5%)
-    """
-    group_a = random.sample(low_roi_ads, 20)
-    group_b = random.sample(low_roi_ads, 20)
-
-    execute_decisions(group_a, bid_change_pct=-0.10)
-    execute_decisions(group_b, bid_change_pct=-0.05)
-
-    # 7天后对比
-    roi_improvement_a = compare_roi(group_a, after=7)
-    roi_improvement_b = compare_roi(group_b, after=7)
-
-    if roi_improvement_a > roi_improvement_b:
-        adopt_strategy("aggressive")
-```
-
-### 4. 多目标优化
-```python
-# 当前: 单一优化ROI
-# 未来: 多目标优化 (ROI + Volume + Risk)
-
-from scipy.optimize import minimize
-
-def objective(bid):
-    roi = predict_roi(bid)
-    volume = predict_volume(bid)
-    risk = calculate_risk(bid)
-
-    # 加权目标函数
-    return -1 * (
-        0.5 * roi +       # 50%权重: ROI
-        0.3 * volume +    # 30%权重: 规模
-        -0.2 * risk       # 20%权重: 风险(负向)
-    )
-
-optimal_bid = minimize(objective, x0=current_bid)
-```
-
----
-
-## 🔧 故障排查指南
-
-### 常见问题1: 数据拉取失败
-```bash
-# 症状
-ERROR - fetch_creative_data失败: No columns to parse from file
-
-# 原因
-• ODPS查询返回空结果
-• 网络连接失败
-• Token过期
-
-# 解决
-1. 检查ODPS连接: odps_client.test_connection()
-2. 检查Token有效性: _get_access_token(account_id)
-3. 查看空文件: ls -lh outputs/raw/*.csv | grep "4B"
-4. 删除空文件: rm outputs/raw/creative_20260416.csv
-5. 重新拉取: python3 execute_once.py
-```
-
-### 常见问题2: 护栏拦截所有决策
-```bash
-# 症状
-护栏验证: blocked 599个, approved 1个
-原因: [数据新鲜度] 数据已过期(58小时前,上限48小时)
-
-# 原因
-使用了过期数据 (20260415), 超过48小时新鲜度上限
-
-# 解决
-1. 拉取最新数据: fetch_creative_data(days=2)
-2. 计算最新ROI: calculate_roi_metrics(end_date="yesterday")
-3. 重新分析: execute_once.py
-```
-
-### 常见问题3: 飞书审批未触发
-```bash
-# 症状
-流程执行完成, 但没有发送飞书消息
-
-# 原因
-• 所有决策被护栏拦截 → 无需审批
-• EXECUTION_ENABLED=False → 不执行操作
-• 飞书Token过期
-
-# 检查
-1. 查看validated_decisions: guardrail_status列是否全是blocked
-2. 检查config.py: EXECUTION_ENABLED = True
-3. 测试飞书API: send_test_message()
-```
-
-### 常见问题4: AI推理偏保守
-```bash
-# 症状
-52个【待评估(候选)】广告, AI只建议暂停2个, 其余全hold
-
-# 原因
-• ROI阈值设置过严格
-• 置信度要求过高
-
-# 调整
-1. 降低关停线: ROI_LOW_FACTOR = 0.5 → 0.6 (config.py)
-2. 修改Skill提示: "对置信度medium的也可以暂停" (roi-strategy.md)
-3. 增加样本: 提供历史决策案例供AI参考
-```
-
----
-
-## 📚 文件结构总览
-
-```
-examples/auto_put_ad_mini/
-├── run.py                   # 交互式运行入口 (支持多轮对话)
-├── execute_once.py          # 单次执行入口 (自动化运行)
-├── config.py                # 核心配置 (阈值、开关、API凭据)
-├── presets.json             # 预设参数
-├── .env                     # 环境变量 (Token、密钥)
-│
-├── prompts/
-│   └── system.prompt        # Agent系统提示词 (模式路由、决策流程)
-│
-├── skills/
-│   ├── roi_strategy.md      # ROI决策框架 (注入给AI的领域知识)
-│   ├── guardrail_rules.md   # 护栏规则说明
-│   └── ad_domain.md         # 广告领域知识 (腾讯广告API、营销概念)
-│
-├── tools/
-│   ├── data_query.py        # 数据拉取+合并 (ODPS → CSV)
-│   ├── roi_calculator.py    # ROI计算 (动态 ROI (7日均值)核心算法)
-│   ├── ad_decision.py       # 决策引擎 (A/B/C分类 + AI决策保存)
-│   ├── guardrails.py        # 安全护栏 (6道检查)
-│   ├── execution_engine.py  # 执行引擎 (调用腾讯广告API)
-│   ├── im_approval.py       # 飞书审批 (阻塞式自然语言审批)
-│   ├── report_generator.py  # 报告生成 (Excel美化)
-│   ├── feishu_doc.py        # 飞书文档导入 (可选)
-│   └── ad_api.py            # 腾讯广告API封装 (底层调用)
-│
-└── outputs/
-    ├── raw/                 # 原始数据 (creative_*.csv, ad_status_*.csv)
-    ├── merged/              # 合并数据 (merged_*.csv)
-    ├── ad_status/           # 广告状态快照
-    ├── reports/             # 决策报告
-    │   ├── llm_decisions_*.csv      # AI原始决策
-    │   ├── validated_decisions_*.csv # 护栏验证后
-    │   ├── decision_*.csv           # 最终决策(CSV)
-    │   └── decision_*.xlsx          # 最终决策(Excel)
-    ├── execution_log/       # 执行审计日志
-    ├── data/
-    │   └── adjustment_history.json  # 调整历史记录
-    └── metrics_*.csv        # 广告级ROI指标 (核心数据)
-```
-
----
-
-## ✅ 总结
-
-auto_put_ad_mini 是一个**生产级的智能广告调控系统**,具备:
-
-1. **智能决策**: AI推理 + 领域知识 + 数据驱动
-2. **安全保障**: 6道护栏 + 分级审批 + 容错降级
-3. **灵活交互**: 自然语言审批 + 3种模式 + 实时修正
-4. **可扩展性**: 双引擎架构 + 模块化设计 + 闭环反馈
-
-**当前状态**: 可独立运行,完成"数据→决策→执行"闭环
-**未来定位**: 接入完整auto_put_ad体系,成为监控调控Agent核心
-
----
-
-**文档版本**: v1.0
-**最后更新**: 2026-04-17
-**作者**: Claude Sonnet 4.5

+ 6 - 3
examples/auto_put_ad_mini/run_decision_test.py

@@ -47,11 +47,14 @@ async def run_decision_test(end_date='20260415'):
         print(f"\n📊 广告分类统计:")
         print(f"   总广告数: {summary.get('total', 0)}")
         print(f"   零消耗待关停: {summary.get('zero_spend_ads', 0)} 个")
-        print(f"   待优化评估: {summary.get('need_review_ads', 0)} 个")
+        print(f"   待优化评估: {data.get('need_review_ads_total', 0)} 个")
         print(f"   正常运行: {summary.get('normal_ads', 0)} 个")
 
-        # 检查待评估广告是否包含新字段
-        need_review_ads = data.get('need_review_ads', [])
+        # 从 tier_batches 提取待评估广告(方案 1:tier 分批)
+        tier_batches = data.get('tier_batches', [])
+        need_review_ads = []
+        for batch in tier_batches:
+            need_review_ads.extend(batch.get('ads', []))
         if need_review_ads:
             print(f"\n✅ 验证新字段(前3个样本):")
             for i, ad in enumerate(need_review_ads[:3]):

+ 8 - 8
examples/auto_put_ad_mini/skills/ad_domain.md

@@ -108,14 +108,14 @@ Day 7~30:         持续贡献
 
 ### R 值对照表
 
-| 人群包 | 带人能力 | 投放单价 | 当日 T0 ROI 表现 | 7 日累积 ROI | 系数 |
-|--------|----------|----------|------------------|--------------|------|
-| R500   | 最强(多日持续带人) | 最高 | **偏低**(价值后置到 Day1~7+) | **最高** | 3.0× |
-| R330+  | 很强             | 较高 | 偏低                           | 高         | 2.5× |
-| R330   | 强               | 中高 | 中偏低                          | 较高       | 2.0× |
-| R180   | 中               | 中   | 中                              | 中上       | 1.5× |
-| R100   | 中弱             | 中低 | 中偏高                          | 中         | 1.2× |
-| R50 / R10 / R2 | 弱(几乎只当天变现) | 低 | 较高(当天即落袋) | 低 | 1.0× |
+| 人群包 | 带人能力 | 投放单价 | 当日 T0 ROI 表现 | 7 日累积 ROI 
+|--------|----------|----------|------------------|--------------
+| R500   | 最强(多日持续带人) | 最高 | **偏低**(价值后置到 Day1~7+) | **最高** 
+| R330+  | 很强             | 较高 | 偏低                           | 高         
+| R330   | 强               | 中高 | 中偏低                          | 较高      
+| R180   | 中               | 中   | 中                              | 中上     
+| R100   | 中弱             | 中低 | 中偏高                          | 中       
+| R50 / R10 / R2 | 弱 | 低 | 较高 | 低 
 
 ### 核心判断原则
 

+ 0 - 1
examples/auto_put_ad_mini/skills/platform_rules.md

@@ -77,7 +77,6 @@ description: 做任何决策前先过一遍本规则——腾讯广告平台的
 ## 5. 小程序场景特殊性
 
 - **优化目标优先 PAGE_VIEW**(页面浏览),比"点击"更接近真实转化路径
-- **归因窗口 15 天**:一次曝光/点击带来的转化可能最晚 15 天后才回流到报表
 - **投放 < 3 天的广告 ROI 必然被低估**:成本侧已跑完,收入侧还没开始回流
 
 **决策含义**:

+ 9 - 2
examples/auto_put_ad_mini/test_single_ad.py

@@ -92,7 +92,7 @@ for skill_name in ["ad_domain", "platform_rules", "decision_strategy", "posterio
 tool_result = json.dumps({
     "summary": {
         "total": 1,
-        "need_review_ads": 1,
+        "need_review_ads_total": 1,
         "tier_groups": 1,
     },
     "distribution": {
@@ -111,7 +111,14 @@ tool_result = json.dumps({
         "bid_down_line": round(CHANNEL_ROI_P50 * 0.90, 4),
         "bid_up_line": round(CHANNEL_ROI_P50 * 1.05, 4),
     },
-    "need_review_ads": [ad_dict],
+    # 按 tier 分批结构(方案 1:tier 分批)
+    "tier_batches": [
+        {
+            "audience_tier": ad_dict["audience_tier"],
+            "count": 1,
+            "ads": [ad_dict],
+        }
+    ],
 }, ensure_ascii=False, indent=2)
 
 user_msg = f"""请对以下 1 条待评估广告做出决策。

+ 5 - 0
examples/auto_put_ad_mini/tools/ad_decision.py

@@ -842,6 +842,11 @@ async def apply_decisions(
         try:
             df_metrics = pd.read_csv(metrics_csv)
             for _, row in df_metrics.iterrows():
+                # ⚠️ 状态过滤:跳过已关停的广告(避免重复决策)
+                ad_status = row.get("configured_status", "")
+                if ad_status in ["AD_STATUS_SUSPEND", "AD_STATUS_DELETED", "SUSPEND", "DELETED"]:
+                    continue
+
                 cost_7d_avg = float(row.get("cost_7d_avg", 0) or 0)
                 ad_age = row.get("ad_age_days")  # 获取广告年龄