Browse Source

refactor(auto_put_ad_mini): skill合并(5→4) + posterior-wisdom改为知识库入口

- 合并 decision-framework + action-playbook → decision-strategy (-415行 → ~330行,去重23%)
- posterior-wisdom 从静态原理(150行) 改为动态知识库查询入口(18行)
- 知识配置预留(enable_extraction=False,投放后开启)
- 同步更新 config.py、presets.json、system.prompt 中 skill 引用

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
刘立冬 3 tuần trước cách đây
mục cha
commit
bd22dcd017

+ 1 - 2
examples/auto_put_ad_mini/config.py

@@ -53,8 +53,7 @@ MAIN_CONFIG = RunConfig(
     skills=[
         "ad-domain",           # 业务模型:裂变模型、R值、ROI公式、字段定义
         "platform-rules",      # 平台硬约束:oCPM学习期、调价上限、数据口径
-        "decision-framework",  # 决策框架:角色定义 + 对比标准 + 候选标记含义 + 年龄策略
-        "action-playbook",     # 动作手册:7种action + 多因素权衡原则
+        "decision-strategy",   # 决策策略:角色 + 基准 + 候选标记 + 年龄策略 + 7种action + 输出规范
         "posterior-wisdom",    # 后验经验:学习中断/降价恢复/创意冷启动/置信度分级
     ],
     extra_llm_params={"max_tokens": 32000},

+ 1 - 1
examples/auto_put_ad_mini/presets.json

@@ -8,6 +8,6 @@
       "calculate_roi_metrics",
       "generate_report"
     ],
-    "skills": ["ad-domain", "platform-rules", "decision-framework", "action-playbook", "posterior-wisdom"]
+    "skills": ["ad-domain", "platform-rules", "decision-strategy", "posterior-wisdom"]
   }
 }

+ 19 - 7
examples/auto_put_ad_mini/prompts/system.prompt

@@ -77,8 +77,10 @@ Step 3: calculate_roi_metrics     ← 计算ROI(依赖Step 1+2的数据)
 Step 4: get_ads_for_review        ← 分类(零消耗待关停 / 需评估 / 正常运行)
 Step 5: AI推理决策                 ← 对【待评估(候选)】广告推理
+         · 参考 roi_zone 和 fission_vs_tier 字段做**综合判断**
+         · ROI 在降价区间时,必须检查裂变率再决定是 bid_down 还是 observe
          · 在**一次 LLM 输出**里为所有候选广告生成完整 JSON 数组(含 ad_id / action / pct / reason / confidence)
-         · 注意力管理:按 tier 分组依次评估,同 tier 内共用同一基线(见下方详细说明)
+         · 注意力管理:按 tier 分组依次评估,同 tier 内共用同一基线
 Step 6: apply_decisions           ← 主 Agent 把第 5 步输出的 JSON 数组整体喂给 apply_decisions
@@ -131,9 +133,20 @@ Step 10: generate_report          ← 生成报告
 - 引用具体数值(ROI/阈值/消耗),用分号连接多个判断
 - `confidence` 与数据支撑度一致;`recommended_change_pct` 为小数(+0.05=提5%),单次绝对值 ≤ 0.10
 
-每条 reason 必须包含 5 个语义元素(ROI 值 / 对比基准 / 偏离% / 辅助信号 / 行动建议),详见 action-playbook skill §五
+每条 reason 必须包含 5 个语义元素(ROI 值 / 对比基准 / 偏离% / 辅助信号 / 行动建议),详见 decision-strategy skill §七
 
-每条 reason 必须体现多维度综合判断——具体维度和权衡原则见 decision-framework、action-playbook、posterior-wisdom skill。
+每条 reason 必须体现多维度综合判断——具体维度和权衡原则见 decision-strategy、posterior-wisdom skill。
+
+### ⚠️ 降价决策的裂变率检查(必须执行)
+
+当 roi_zone = "bid_down_zone" 时,**必须检查 fission_vs_tier 再做决策**:
+
+- fission_vs_tier = "low" → 可以 bid_down(ROI低+裂变低,双低确认)
+- fission_vs_tier = "normal" → 改 observe(裂变正常,ROI低可能暂时)
+- fission_vs_tier = "high" → 改 observe 或 hold(裂变优秀,有长期价值)
+- fission_vs_tier = "unknown" → 改 observe(数据不足不决策)
+
+**禁止**:仅因 roi_zone="bid_down_zone" 就判定 bid_down,必须结合裂变信号。
 
 **硬约束**:
 - reason 中禁止出现英文变量名(pause_line、bid_down_line、tier_roi_p50 等),改用中文术语
@@ -143,17 +156,16 @@ Step 10: generate_report          ← 生成报告
 
 # 第六部分:投放经验知识库(Skills)
 
-你有 5 份 skill(由框架自动注入,不需要主动查询):
+你有 4 份 skill(由框架自动注入,不需要主动查询):
 
 | skill | 核心内容 |
 |-------|---------|
 | **ad-domain** | 裂变模型、R值含义、ROI公式、核心字段定义 |
 | **platform-rules** | 腾讯 oCPM 学习期、调价上限、数据口径(平台硬约束 > 业务决策) |
-| **decision-framework** ⭐ | 角色定义(综合决策者≠规则复读机)、对比标准、候选标记含义、年龄策略 |
-| **action-playbook** ⭐ | 7 种 action 触发前提 + 权衡要点 + 输出规范与自检 |
+| **decision-strategy** ⭐ | 角色定位 + 对比基准 + 候选标记 + 年龄策略 + 7种action权衡 + 输出规范 |
 | **posterior-wisdom** | 学习中断 / 降价恢复期 / 创意冷启动 / ROI 置信度分级 |
 
-Skill 提供「判断原则」,工具提供「数据」,你负责综合判断。冲突优先级:platform-rules > decision-framework > action-playbook > posterior-wisdom > ad-domain。
+Skill 提供「判断原则」,工具提供「数据」,你负责综合判断。冲突优先级:platform-rules > decision-strategy > posterior-wisdom > ad-domain。
 
 # 第七部分:与您的对话
 

+ 1 - 1
examples/auto_put_ad_mini/quick_analysis.py

@@ -73,7 +73,7 @@ async def main():
             for i, ad in enumerate(review_data['b_ads'][:3], 1):
                 print(f"\n  {i}. 广告 {ad.get('ad_id')}")
                 print(f"     ROI: {ad.get('动态ROI_7日均值', 0):.2f}  消耗: {ad.get('cost_7d_avg', 0):.0f}元/天")
-                print(f"     建议: {ad.get('bid_candidate', 'N/A')}")
+                print(f"     ROI区间: {ad.get('roi_zone', 'N/A')}  裂变对比: {ad.get('fission_vs_tier', 'N/A')}")
     else:
         print("⚠️  无法解析决策结果")
 

+ 0 - 130
examples/auto_put_ad_mini/skills/decision_framework.md

@@ -1,130 +0,0 @@
----
-name: decision-framework
-description: 每次为候选广告做决策前必读——明确你作为"综合决策者"的角色(不是规则复读机)、ROI/裂变的对比基准口径、候选标记的业务含义、年龄保护策略
----
-
-# 决策框架(每次决策前必读)
-
-> **SSOT 声明**:所有阈值数值以 `config.py` 为准。本文档只描述概念与角色分工。
-
----
-
-## 一、你的角色 — 综合决策者,不是规则复读机
-
-```
-规则层(体检仪器)      →  候选标记(体检报告)   →  你(医生)          
-ad_decision.py              roi_low=True 等           综合全局信息判断     
-```
-
-### 你的职责
-
-- ✅ **理解候选标记的业务含义**(如"roi_low=True"表示"ROI 严重偏低")
-- ✅ **综合多因素做最终判断**:裂变表现、调整历史、tier组合位置、数据稳定性、异常模式
-- ✅ **给出可解释的 reason**:说清楚"为什么做这个判断",让运营能追溯逻辑
-
-
-### 核心原则
-
-规则告诉你"体检报告上有异常指标",你决定"是真的需要治疗,还是先观察"。
-
-roi_low=True 不等于必须 pause —— 可能数据不稳定、可能刚调过价还没见效、可能这是 tier 里唯一的跑量广告。
-bid_up_candidate=True 也不等于必须 bid_up —— 可能 CTR 其实在下滑、可能近期已提过价。
-
-**你的判断 > 规则的候选**,但要给出充分理由。
-
----
-
-## 二、对比标准(不同维度用不同基准,严禁混用)
-
-| 维度 | 对比基准 | 数据字段 | 业务含义 |
-|------|---------|---------|---------|
-| 动态 ROI | **渠道P50** | `channel_roi_p50` | 全体广告"动态ROI_7日均值"的中位数 = 全渠道整体水位 |
-| 裂变率(fission_rate) | **同类均值** | `tier_fission_mean` | 同人群包 R 值的裂变水位 |
-| CTR | **同类均值** | 同类 | 同人群的曝光质量 |
-
-### 为什么 ROI 必须看渠道
-
-- 渠道P50 反映当前大盘的"合理回报"
-- 预算是跨人群共享的,低于 P50 就是跑不出渠道平均效率,应优化或淘汰
-- 某人群同类中位数不等于"达标线"(低价值人群同类中位数低,不代表"达标")
-
-### 为什么裂变率必须看同类
-
-- 不同人群的裂变天然不同(R500 高价值人群裂变弱、R50 宽泛人群裂变强)
-- 只有同 R 值人群的裂变均值才能衡量"这条广告在它的人群里裂变强不强"
-
-### reason 中的术语约定
-
-**禁止**在 reason 中出现英文变量名,统一用中文术语:
-
-| 禁用 | 必用 |
-|------|------|
-| `pause_line` | 关停线 |
-| `bid_down_line` | 降价线 |
-| `bid_up_line` | 提价线 |
-| `channel_roi_p50` / `roi_mean` | 渠道P50 / 渠道中位数 |
-| `tier_fission_mean` | 同类均值 / 同类裂变均值 |
-| `bid_increased_7d` | 7 天内已提价 |
-| `creative_changed_7d` | 7 天内已换创意 |
-| `roi_valid_days` | ROI 有效天数 |
-
----
-
-## 三、候选标记含义(规则层的输出,不要重复检查)
-
-规则层(`ad_decision.py`)已经完成了以下检测,并把结果以候选标记的形式传递给你:
-
-| 候选标记 | 含义 | 规则已检测的内容 |
-|---------|------|----------------|
-| `roi_low=True` | ROI 严重偏低 | 动态ROI 明显低于渠道P50(具体系数由规则控制) + 昨日消耗达标 + 年龄达标 |
-| `bid_up_candidate=True` | 有提价潜力 | ROI 明显优于渠道 + 年龄在提价窗口 + 消耗未过高 + CTR 达标 |
-| `bid_down_candidate=True` | 需要降价优化 | ROI 略低于渠道但未到关停线 + 消耗足够 + 近期无调价/换创意 |
-| `scale_up_candidate=True` | 值得扩量 | 成熟稳定 + 高消耗 + ROI 达标 |
-| `decay_signal=True` | 有衰退迹象 | 消耗趋势下降或 ROI 持续走低 |
-
-### 阈值参考(仅用于理解规则逻辑,不要在 reason 中引用具体数值)
-
-- **关停线** ≈ 渠道P50 × 0.75 — roi_low 的触发线
-- **降价线** ≈ 渠道P50 × 0.90 — bid_down_candidate 的触发线
-- **提价线** ≈ 渠道P50 × 1.05 — bid_up_candidate 的触发线
-
-(以上数值可能随运营策略调整,**以规则输出的候选标记为准**,不要硬记数值)
-
-### 你看到的数据
-
-每条候选广告到你手里时,已携带完整上下文:
-- 候选标记(上述 5 个 bool)
-- 核心指标(动态ROI、7日均消耗、昨日消耗、广告年龄、创意数等)
-- 渠道基准(channel_roi_p50)
-- 同类基准(tier_fission_mean、tier 内广告数/消耗统计)
-- 调整历史(7天内是否提价/降价/换创意)
-- 数据质量(roi_valid_days、stable_spend_days_30d)
-
-**你的工作是综合这些信息做判断,而不是只看候选标记就机械输出 action。**
-
----
-
-## 四、年龄策略
-
-> 年龄分段以 `config.py::COLD_START_DAYS`(默认 3)和 `EARLY_GROWTH_DAYS`(默认 7)为准。
-
-| 年龄段 | 天数 | 你会看到吗 | 允许操作 | 禁止操作 | 原因 |
-|--------|------|-----------|---------|---------|------|
-| **冷启动期** | ≤3天 | 不会(规则已排除) | — | 所有操作 | 系统刚开始学习,任何调整都会打断 |
-| **早期成长期** | 4-7天 | 仅提价/观察候选 | bid_up / observe / creative_adjust | bid_down / pause | 降价会打断 oCPM 学习,关停更不行 |
-| **成熟期** | >7天 | 正常候选 | 全部 action | bid_up(改走 scale_up) | 投手经验:稳定期不调出价,通过新增广告/创意拿量 |
-
-**说明**:年龄保护由规则层 + 护栏层双重实现。你收到的候选广告已经过年龄筛选,不太可能收到违反年龄保护的广告。万一收到,护栏会兜底拦截。
-
----
-
-## 五、action 与候选标记的一致性(硬约束)
-
-| 候选标记 | 允许的 action | 禁止的 action | 原因 |
-|---------|-------------|-------------|------|
-| `bid_up_candidate=True` | `bid_up` / `hold` / `observe` | ❌ `bid_down` / `pause` | 规则标记为"优质",降价或关停自相矛盾 |
-| `roi_low=True` | `pause` / `observe` / `hold` | ❌ `bid_up` / `scale_up` | 规则标记为"ROI 严重偏低",不能加码 |
-| `bid_down_candidate=True` | `bid_down` / `hold` / `observe` | ❌ `bid_up` | 规则标记为"需降价",提价自相矛盾 |
-| `scale_up_candidate=True` | `scale_up` / `hold` / `observe` | ❌ `pause` / `bid_down` | 规则标记为"值得扩量",关停或降价矛盾 |
-
-**护栏会检查这张表**(ActionConsistencyGuardrail),违反会被拦截。但不要依赖护栏——让护栏 0 告警是目标。

+ 166 - 33
examples/auto_put_ad_mini/skills/action_playbook.md → examples/auto_put_ad_mini/skills/decision_strategy.md

@@ -1,39 +1,148 @@
 ---
-name: action-playbook
-description: 选择action前必读——7种action的触发前提与权衡要点、多因素综合判断原则(不是机械的if-then决策树,而是权衡框架)
+name: decision-strategy
+description: 广告决策完整策略——角色定位、对比基准、候选标记、年龄策略、7种action权衡、输出规范与自检
 ---
 
-# 决策动作手册
+# 决策策略(每次决策前必读)
 
-> **前置阅读**:`decision-framework`(候选标记含义与对比基准)、`platform-rules`(平台硬约束先过一遍)
+> **SSOT 声明**:所有阈值数值以 `config.py` 为准。本文档只描述概念、角色分工与判断原则。
 
 ---
 
-## 一、候选标记 → action 映射(取代旧决策树)
+## 一、角色与原则
 
+### 三层架构
+
+```
+规则引擎(自动检测)    →  候选标记(信号输出)   →  你(综合决策者)
+ad_decision.py              roi_low=True 等           结合上下文做最终判断
 ```
-收到候选广告
+
+### 你的职责
+
+- ✅ **理解候选标记的业务含义**(如"roi_low=True"表示"ROI 严重偏低")
+- ✅ **综合多因素做最终判断**:裂变表现、调整历史、tier组合位置、数据稳定性、异常模式
+- ✅ **给出可解释的 reason**:说清楚"为什么做这个判断",让运营能追溯逻辑
+
+### 核心原则
+
+规则引擎负责阈值检测和信号标记,你负责结合完整上下文做最终判断——信号是参考输入,不是执行指令。
+
+roi_low=True 不等于必须 pause —— 可能数据不稳定、可能刚调过价还没见效、可能这是 tier 里唯一的跑量广告。
+bid_up_candidate=True 也不等于必须 bid_up —— 可能 CTR 其实在下滑、可能近期已提过价。
+
+**你的判断 > 规则的候选**,但要给出充分理由。
+
+### 对比标准(不同维度用不同基准,严禁混用)
+
+| 维度 | 对比基准 | 数据字段 | 业务含义 |
+|------|---------|---------|---------|
+| 动态 ROI | **渠道P50** | `channel_roi_p50` | 全体广告"动态ROI_7日均值"的中位数 = 全渠道整体水位 |
+| 裂变率(fission_rate) | **同类均值** | `tier_fission_mean` | 同人群包 R 值的裂变水位 |
+| CTR | **同类均值** | 同类 | 同人群的曝光质量 |
+
+**为什么 ROI 必须看渠道**:渠道P50 反映当前大盘的"合理回报"。预算是跨人群共享的,低于 P50 就是跑不出渠道平均效率,应优化或淘汰。某人群同类中位数低,不代表"达标"。
+
+**为什么裂变率必须看同类**:不同人群的裂变天然不同(R500 高价值人群裂变弱、R50 宽泛人群裂变强)。只有同 R 值人群的裂变均值才能衡量"这条广告在它的人群里裂变强不强"。
+
+### reason 中的术语约定
+
+**禁止**在 reason 中出现英文变量名,统一用中文术语:
+
+| 禁用 | 必用 |
+|------|------|
+| `pause_line` | 关停线 |
+| `bid_down_line` | 降价线 |
+| `bid_up_line` | 提价线 |
+| `channel_roi_p50` / `roi_mean` | 渠道P50 / 渠道中位数 |
+| `tier_fission_mean` | 同类均值 / 同类裂变均值 |
+| `bid_increased_7d` | 7 天内已提价 |
+| `creative_changed_7d` | 7 天内已换创意 |
+| `roi_valid_days` | ROI 有效天数 |
+
+---
+
+## 二、候选标记解读(规则层的输出,不要重复检查)
+
+规则层(`ad_decision.py`)已经完成了以下检测,并把结果以候选标记的形式传递给你:
+
+| 字段 | 含义 | 规则已检测的内容 |
+|---------|------|----------------|
+| `roi_zone` | ROI 所在区间(客观事实) | `below_pause_line` / `bid_down_zone` / `above_bid_up_line` / `normal` |
+| `fission_vs_tier` | 裂变率与同类对比 | `high`(≥同类110%) / `normal` / `low`(<同类90%) / `unknown`(数据缺失) |
+| `bid_up_candidate=True` | 有提价潜力 | ROI 明显优于渠道 + 年龄在提价窗口 + 消耗未过高 + CTR 达标 |
+| `scale_up_candidate=True` | 值得扩量 | 成熟稳定 + 高消耗 + ROI 达标 |
+| `decay_signal=True` | 有衰退迹象 | 消耗趋势下降或 ROI 持续走低 |
+
+**⚠️ 关键变化**:`roi_zone="bid_down_zone"` 不等于"应该降价"——必须结合 `fission_vs_tier` 综合判断(见§四决策映射)。
+
+### 阈值参考(仅用于理解规则逻辑,不要在 reason 中引用具体数值)
+
+- **关停线** ≈ 渠道P50 × 0.75 — roi_low 的触发线
+- **降价线** ≈ 渠道P50 × 0.90 — bid_down_candidate 的触发线
+- **提价线** ≈ 渠道P50 × 1.05 — bid_up_candidate 的触发线
+
+(以上数值可能随运营策略调整,**以规则输出的候选标记为准**,不要硬记数值)
+
+### 你看到的数据
+
+每条候选广告到你手里时,已携带完整上下文:
+- 候选标记(上述 5 个 bool)
+- 核心指标(动态ROI、7日均消耗、昨日消耗、广告年龄、创意数等)
+- 渠道基准(channel_roi_p50)
+- 同类基准(tier_fission_mean、tier 内广告数/消耗统计)
+- 调整历史(7天内是否提价/降价/换创意)
+- 数据质量(roi_valid_days、stable_spend_days_30d)
+
+**你的工作是综合这些信息做判断,而不是只看候选标记就机械输出 action。**
+
+---
+
+## 三、年龄策略
+
+> 年龄分段以 `config.py::COLD_START_DAYS`(默认 3)和 `EARLY_GROWTH_DAYS`(默认 7)为准。
+
+| 年龄段 | 天数 | 你会看到吗 | 允许操作 | 禁止操作 | 原因 |
+|--------|------|-----------|---------|---------|------|
+| **冷启动期** | ≤3天 | 不会(规则已排除) | — | 所有操作 | 系统刚开始学习,任何调整都会打断 |
+| **早期成长期** | 4-7天 | 仅提价/观察候选 | bid_up / observe / creative_adjust | bid_down / pause | 降价会打断 oCPM 学习,关停更不行 |
+| **成熟期** | >7天 | 正常候选 | 全部 action | bid_up(改走 scale_up) | 投手经验:稳定期不调出价,通过新增广告/创意拿量 |
+
+**说明**:年龄保护由规则层 + 护栏层双重实现。你收到的候选广告已经过年龄筛选,不太可能收到违反年龄保护的广告。万一收到,护栏会兜底拦截。
+
+---
+
+## 四、信号 → action 映射
+
+```
+收到候选广告(附带 roi_zone / fission_vs_tier / bid_up_candidate 等客观信号)
-  ├─ roi_low=True
-  │   └─ 综合权衡后 → pause / observe / hold(第二章 §1)
+  ├─ roi_zone = "below_pause_line"(ROI 严重偏低)
+  │   └─ 综合权衡后 → pause / observe / hold(§五.1)
-  ├─ bid_down_candidate=True
-  │   └─ 综合权衡后 → bid_down / observe / hold(第二章 §2)
+  ├─ roi_zone = "bid_down_zone"(ROI 在降价区间)
+  │   ├─ fission_vs_tier = "low"    → 双低确认 → bid_down 3%-5%(§五.2)
+  │   ├─ fission_vs_tier = "normal" → observe(ROI 低但裂变正常,需观察)
+  │   ├─ fission_vs_tier = "high"   → observe / hold(裂变优秀,ROI 低可能暂时)
+  │   └─ fission_vs_tier = "unknown" → observe(数据不足不决策)
-  ├─ bid_up_candidate=True
-  │   └─ 综合权衡后 → bid_up / observe / hold(第二章 §3)
+  ├─ bid_up_candidate = True
+  │   └─ 综合权衡后 → bid_up / observe / hold(§五.3)
-  ├─ scale_up_candidate=True
-  │   └─ 综合权衡后 → scale_up / observe / hold(第二章 §4)
+  ├─ scale_up_candidate = True
+  │   └─ 综合权衡后 → scale_up / observe / hold(§五.4)
   └─ 以上都不满足 → hold 或 observe
 ```
 
-**关键理念**:候选标记是规则层的"推荐",不是"命令"。你需要综合权衡后做最终判断。
+**关键理念**:
+- `roi_zone` 和 `fission_vs_tier` 是客观事实,不是操作建议
+- `roi_zone="bid_down_zone"` **不等于**"应该降价"——必须结合裂变率综合判断
+- 候选标记是规则层的"推荐",不是"命令"。你需要综合权衡后做最终判断
 
 ---
 
-## 二、7 种 action 详解
+## 、7 种 action 详解
 
 ### 1. `pause`(关停)— 明确低效,释放预算
 
@@ -68,22 +177,33 @@ description: 选择action前必读——7种action的触发前提与权衡要点
 
 ---
 
-### 2. `bid_down`(降价)— ROI 偏低但有优化空间
+### 2. `bid_down`(降价)— ROI 偏低 + 裂变偏低,双低确认
 
-**触发前提**:`bid_down_candidate=True`(规则已确认 ROI 略低于渠道但未到关停线、消耗足够、近期无调价/换创意
+**触发前提**:`roi_zone="bid_down_zone"`(ROI 在降价区间)+ `fission_vs_tier="low"`(裂变低于同类
 
-**你需要综合权衡的要点**:
+> ⚠️ **核心原则:bid_down 需要"ROI 低 + 裂变低"双重确认。单凭 ROI 低不足以降价。**
 
-1. **裂变辅助信号**:裂变率低于同类均值 → 降价更合理(ROI低+裂变低=效率确实差)
-2. **近期是否调过价**:规则已检查 7 天无调价,但如果 reason 中提及历史调价效果,更有说服力
-3. **消耗稳定性**:稳定天数较少时,降幅取下限(保守)
-4. **ROI 置信度**:有效天数较少时,降幅取下限
+**条件(全部满足才能 bid_down)**:
+- ✅ 年龄 > 7 天(成熟期)
+- ✅ 7 日均消耗 ≥ 500 元
+- ✅ roi_zone = "bid_down_zone"(关停线 ≤ 动态ROI < 降价线)
+- ✅ **fission_vs_tier = "low"**(裂变率低于同类均值 10%+)—— 核心条件
+
+**⚠️ 即使 ROI 在降价区间,以下场景禁止降价**:
+- ❌ fission_vs_tier = "high"(裂变优秀)→ 改 observe 或 hold
+- ❌ fission_vs_tier = "normal"(裂变正常)→ 改 observe
+- ❌ fission_vs_tier = "unknown"(数据缺失)→ 改 observe
+- ❌ 近 7 天已降过价 → 改 observe(等效果显现)
+- ❌ 近 7 天换过创意 → 改 observe(等数据稳定)
+
+**业务逻辑**:裂变率高 = 用户自传播能力强 = 长期 ROI 潜力大。降价会减少曝光、降低 oCPM 学习效率、浪费优质广告潜力。
 
 **pct 要求**:负数,绝对值在 [3%, 5%]
 
 **幅度选择原则**:
 - ROI 偏离渠道P50 较小 → 降 3%(轻度优化)
 - ROI 偏离渠道P50 较大(接近关停线)→ 降 5%(上限)
+- 消耗稳定性或 ROI 置信度低 → 降幅取下限
 - 更严重的低效走 pause,不要用"大幅降价"代替关停
 
 **禁用于**:`bid_up_candidate=True` 的广告
@@ -170,9 +290,9 @@ description: 选择action前必读——7种action的触发前提与权衡要点
 
 ---
 
-## 三、多因素权衡原则(规则无法覆盖的决策维度)
+## 六、多因素权衡(规则无法覆盖的决策维度)
 
-这是你作为"医生"真正需要发挥的价值:
+这是你真正需要发挥的价值:
 
 ### 1. 裂变 vs ROI 双低判断
 
@@ -208,9 +328,7 @@ description: 选择action前必读——7种action的触发前提与权衡要点
 - **能用 creative_adjust 解决的问题,不要用 pause** —— 保留 oCPM 学习资产
 - **降价和关停之间,优先关停** —— 干净的止损,不要用"大幅降价"代替
 
----
-
-## 四、action 选择优先级
+### 5. action 选择优先级
 
 当多个 action 都"合理"时,按以下优先级取:
 
@@ -224,11 +342,26 @@ pause(明确低效)> bid_down(有改善空间)> creative_adjust(素材
 bid_up(冷启动期优质)> scale_up(成熟期优质)
 ```
 
+### 6. ROI 与裂变信号冲突时的处理
+
+当 ROI 信号和裂变信号方向不一致时:
+
+| ROI 信号 | 裂变信号 | 决策 | 理由 |
+|---------|---------|------|------|
+| 低于降价线 | fission_vs_tier="high" | observe/hold | 裂变优秀,ROI低可能是短期波动 |
+| 低于降价线 | fission_vs_tier="normal" | observe | 裂变正常,需观察ROI是否持续低 |
+| 低于降价线 | fission_vs_tier="low" | bid_down | 双低确认,降价合理 |
+| 低于关停线 | fission_vs_tier="high" | observe | 裂变优秀,先观察不急于关停 |
+| 低于关停线 | fission_vs_tier="low" | pause | 双低+ROI极低,关停合理 |
+
+**冲突时 reason 必须显式说明**:
+> "动态ROI为X.XX,低于降价线Y.YY;但裂变率Z.ZZ高于同类均值W.WW的N%,广告质量潜力高,建议观察而非降价"
+
 ---
 
-## 五、决策输出规范与自检
+## 、决策输出规范与自检
 
-### 5.1 reason 5 元组(每条必含)
+### 7.1 reason 5 元组(每条必含)
 
 **每条 reason 必须显式包含以下 5 个语义元素**,缺任一项视为不合格:
 
@@ -256,7 +389,7 @@ bid_up(冷启动期优质)> scale_up(成熟期优质)
 > ❌ "动态ROI=1.62 < pause_line(1.66), bid_increased_7d=true"(用英文变量名,违反硬约束)
 > ❌ "ROI 不好,建议降价"(缺数值、基准、偏离%、辅助信号)
 
-### 5.2 action 与 recommended_change_pct 的强绑定
+### 7.2 action 与 recommended_change_pct 的强绑定
 
 | action | recommended_change_pct | 违反时修正 |
 |---|---|---|
@@ -267,13 +400,13 @@ bid_up(冷启动期优质)> scale_up(成熟期优质)
 | `scale_up` | ≥ 0(可 0,可正) | 扩量不降价,不得为负 |
 | `creative_adjust` | = 0 | 创意动作,不改出价 |
 
-### 5.3 reason 与 action 语义一致
+### 7.3 reason 与 action 语义一致
 
 - reason 含 **"表现优秀/保持/维持/微调"** → action **不可以**是 `pause`/`bid_down`
 - reason 含 **"严重低迷/持续亏损/关停线"** → action **不可以**是 `hold`/`bid_up`/`scale_up`
 - reason 含 **"建议保持或微调"** → action 只能是 `hold` 或 `observe`,**绝不能**是 `bid_down`
 
-### 5.4 提交前自检(内心默问)
+### 7.4 提交前自检(内心默问)
 
 1. 我这条 action 和 pct 数字方向对得上吗?(降价↔负数,提价↔正数,维持↔零)
 2. 我的 reason 结论和 action 语义一致吗?(说"优秀"还建议降价吗?)

+ 11 - 143
examples/auto_put_ad_mini/skills/posterior_wisdom.md

@@ -1,150 +1,18 @@
 ---
 name: posterior-wisdom
-description: reason 里需要解释"为什么建议观察而非立即操作"时查阅——提供平台机制原理 + 知识库查询入口
+description: 需要引用历史投放案例或经验规律时查阅——通过 ask_knowledge() 查询知识库,无数据时跳过
 ---
 
-# 投放后验经验(解释"为什么不立即操作")
+# 后验经验查询
 
-> **使用时机**:reason 里需要解释"为什么建议观察而非立即调价/关停"时查阅本 skill。
->
-> **本 skill 的定位**:
-> - **通用原理**(下文第一~四章):基于腾讯 oCPM 平台机制的确定性知识,不随投放变化
-> - **动态经验**(第五章):来自知识库的真实投放后验数据,随投放积累持续更新
->
-> 你不需要记住所有后验细节,只需理解原理框架,具体数据通过 `ask_knowledge()` 获取。
+系统尚未积累足够的投放后验数据。当决策需要参考历史经验时,通过 `ask_knowledge()` 查询知识库:
 
----
-
-## 一、连续调价的学习中断(系统稳定性原理)
-
-### 理论基础
-每次调价都会触发系统重新学习,频繁调价会导致:
-- 学习周期被打断,无法收敛到最优状态
-- 系统在多个出价点之间徘徊,效率降低
-
-### 决策框架
-1. **避免短期内(7 天)频繁调价**(≥3 次)
-2. 每次调价后,**至少观察 5-7 天**再考虑下次调价
-3. 广告 ROI 好但已多次提价 → **暂缓提价,观察稳定性**
-4. 连续调价效果递减是 **系统原理**,不是偶然现象
-
-### 判断要点
-- 检查近 7 天调价历史(字段 `bid_increased_7d`、`adjustment_count_7d`)
-- 已调价 ≥2 次 → 倾向 observe 而非"继续调价"
-- 给系统足够的稳定周期
-
----
-
-## 二、降价后的流量恢复期(竞价机制原理)
-
-### 理论基础
-降价后,系统需要重新分配流量(CPM 降低 → 曝光机会减少),ROI 改善需要时间:
-- 系统调整投放策略(人群、时段、版位)
-- 流量质量重新筛选
-
-### 理论预期
-- **即时效果**:降价不会立即改善 ROI(第 1 天可能没变化)
-- **延迟显现**:需要 2-5 天,ROI 才能稳定改善
-- **消耗下降**:降价后消耗通常会同步下降
-
-### 决策框架
-1. 降价后 **1-2 天内 ROI 未改善** → 正常,继续观察
-2. 降价后 **3-5 天仍无改善** → 可能问题不在出价,考虑其他因素(创意/定向)
-3. 降价后 **消耗大幅下降但 ROI 仍低** → 建议关停(低效广告)
-
----
-
-## 三、创意更换的冷启动期(新素材学习原理)
-
-### 理论基础
-新创意素材需要冷启动学习期,系统需要:
-- 测试创意对不同人群的吸引力
-- 探索最优投放策略
-
-### 理论预期
-- **前 3-7 天**:数据波动大,ROI 可能忽高忽低
-- **7 天后**:效果趋于稳定,可以评估创意质量
-
-### 决策框架
-1. 广告**近 7 天内换过创意** → 所有数据仅供参考
-2. 创意冷启动期内:
-   - ROI 突然很高 → 不要立即提价(可能是偶然)
-   - ROI 突然很低 → 不要立即关停(系统还在学习)
-3. **至少观察 7 天**后,再基于新创意数据做决策
-
-### 判断要点
-- 检查字段 `creative_changed_7d`(布尔)或 `creative_days_since_change`
-- < 7 天 → 谨慎决策,不要大幅调整
-- ≥ 7 天 → 数据已稳定,可以正常决策
-
----
-
-## 四、ROI 数据置信度分级
-
-由于支持"不足 7 天用几天"的 ROI 计算(`min_periods=3`),需要根据有效数据天数评估置信度。
-
-### 置信度分级
-
-| roi_valid_days | 置信度 | 决策建议 |
-|---------------|--------|---------|
-| ≥ 7 天 | 高 | 可正常决策 |
-| 4-6 天 | 中 | 谨慎决策,避免激进操作 |
-| 3 天 | 低 | 仅做保守决策(明显异常才关停) |
-| < 3 天 | 无 | ROI=NaN,无法决策 |
-
-### 置信度对决策的影响
-
-**高置信度(≥7 天)**:正常执行各类操作
-
-**中等置信度(4-6 天)**:降价/提价取幅度下限,关停需更严格条件
-
-**低置信度(3 天)**:
-- 优先 `observe` 而非 bid_down/bid_up
-- 仅明显异常才关停(动态ROI 远低于关停线)
-- reason 中必须说明"数据不足"
-
----
-
-## 五、动态后验经验(知识库查询入口)
-
-> 以下经验需要通过真实投放数据持续验证和更新。
-> 系统会自动将每轮决策中的后验发现提取到知识库。
-
-### 查询方式
-
-当你在决策中需要以下信息时,使用 `ask_knowledge()` 查询:
-
-| 需要的信息 | 查询示例 | 说明 |
-|-----------|---------|------|
-| 周期性效应 | `ask_knowledge("周末效应 ROI 波动规律")` | 周末/月初月末的流量差异 |
-| 调价效果验证 | `ask_knowledge("降价后ROI恢复 实际案例")` | 降价/提价后的真实效果追踪 |
-| 人群包表现差异 | `ask_knowledge("R330 vs R180 裂变效率对比")` | 不同 tier 的历史表现 |
-| 异常模式识别 | `ask_knowledge("CTR正常但ROI低 历史案例")` | 已识别的异常模式库 |
-| 创意冷启动验证 | `ask_knowledge("创意更换后ROI稳定周期")` | 实际观察到的冷启动时长 |
-
-### 已知待验证假设
-
-以下是基于行业经验的先验假设,**尚未用我们的投放数据验证**。
-决策时可参考,但不可作为唯一依据:
-
-1. **周末效应**:周六日流量构成与工作日不同,ROI 可能自然偏高或偏低
-2. **月初/月末效应**:月初竞争激烈(CPM 偏高),月末竞争降低(CPM 偏低)
-3. **连续调价衰减曲线**:第 3 次调价的边际效果是否显著低于第 1 次
-
-> 随着投放数据积累,这些假设会被验证或修正,结果自动存入知识库。
-
----
-
-## 六、反例警示(避免模板化的 reason)
-
-### 反例 1:只写结论不写依据
-**❌ 错**:"ROI 为 1.80,低于降价线 2.65,建议降 5%"
-**✅ 对**:"动态 ROI 为 1.80,低于渠道P50 2.65 的 32%;7 天内已提价但 ROI 仍低迷,判断调价无效;7 日日均消耗 4438 元属于高消耗,建议关停"
-
-### 反例 2:忽略数据置信度
-**❌ 错**:"ROI=1.25 < 关停线(1.36),建议关停"
-**✅ 对**:"动态 ROI 为 1.25,低于关停线 1.36;但 30 天内仅 3 天消耗稳定数据波动较大,且广告仅投放 7 天仍在学习期,建议观察而非立即关停"
+| 需要的信息 | 查询示例 |
+|-----------|---------|
+| 周期性效应 | `ask_knowledge("周末效应 ROI 波动规律")` |
+| 调价效果 | `ask_knowledge("降价后ROI恢复 实际案例")` |
+| 人群包表现 | `ask_knowledge("R330 vs R180 裂变效率对比")` |
+| 异常模式 | `ask_knowledge("CTR正常但ROI低 历史案例")` |
+| 创意冷启动 | `ask_knowledge("创意更换后ROI稳定周期")` |
 
-### 反例 3:混用英文变量名
-**❌ 错**:"动态ROI=1.62 略低于 pause_line(1.66),bid_increased_7d=true 但 ROI 仍低迷,ad_age=9 天,判断为调价无效"
-**✅ 对**:"动态 ROI 为 1.62,略低于关停线 1.66;7 天内已提价但 ROI 仍低迷;广告已投放 9 天,7 日日均消耗 4438 元属于高消耗广告;综合判断调价无效,建议关停"
+知识库暂无数据时,跳过查询,基于当前指标正常决策即可。

+ 4 - 2
examples/auto_put_ad_mini/test_single_ad.py

@@ -35,7 +35,9 @@ ad_dict = {
     "creative_changed_7d": True,
     "stable_spend_days_30d": 9,
     "bid_amount": 0.39,
-    "bid_candidate": None,  # 不是 bid_up/bid_down 候选(因裂变高不满足 bid_down 条件)
+    "roi_zone": "bid_down_zone",  # ROI 在降价区间(裂变判断交给 LLM)
+    "fission_vs_tier": "high",    # 裂变率高于同类均值 10%+
+    "bid_up_candidate": False,
     "scale_up_candidate": False,
     "ad_fission": 1.61,
     "ad_ctr": 0.032,
@@ -77,7 +79,7 @@ system_prompt = (MINI_DIR / "prompts" / "system.prompt").read_text()
 
 skills_text = ""
 skills_dir = MINI_DIR / "skills"
-for skill_name in ["roi_baseline", "action_playbook", "age_protection", "guardrail_rules"]:
+for skill_name in ["ad_domain", "platform_rules", "decision_strategy", "posterior_wisdom"]:
     fpath = skills_dir / f"{skill_name.replace('-', '_')}.md"
     if not fpath.exists():
         fpath = skills_dir / f"{skill_name.replace('_', '-')}.md"

+ 19 - 7
examples/auto_put_ad_mini/tools/ad_decision.py

@@ -482,15 +482,14 @@ async def get_ads_for_review(
             # 命中任一分支即视为提价候选(OR 关系,两条经验路径独立有效)
             bid_up_candidate = bid_up_candidate_a or bid_up_candidate_b
 
-            # 降价:ROI低于渠道均值10% + 裂变低于同类10% + 消耗≥500元/天
+            # 降价候选(入池):ROI 在降价区间 + 消耗≥500元/天
+            # ★ 裂变率条件已移至 LLM 层判断(fission_vs_tier 字段),规则层只负责入池
             bid_down_candidate = (
                 (not pd.isna(dynamic_roi_7d))
-                and dynamic_roi_7d < channel_roi_p50 * params["BID_DOWN_ROI_FACTOR"]      # ROI低于渠道均值10%
+                and dynamic_roi_7d < channel_roi_p50 * params["BID_DOWN_ROI_FACTOR"]      # ROI 在降价区间
                 and dynamic_roi_7d >= channel_roi_p50 * params["ROI_LOW_FACTOR"]          # 但未达关停线
-                and cost_7d_avg >= BID_DOWN_MIN_SPEND                     # 消耗≥500元/天
+                and cost_7d_avg >= BID_DOWN_MIN_SPEND                                     # 消耗有数据意义
                 and bid_amount > 0
-                and (tier_fission_mean is None or ad_fission is None      # 裂变低于同类均值10%(无数据时跳过)
-                     or ad_fission < tier_fission_mean * 0.90)
             ) if BID_ADJUSTMENT_ENABLED else False
 
             # ===== 持续低ROI升级关停(投手经验2.4:"降价后持续低于均值就关停")=====
@@ -599,8 +598,21 @@ async def get_ads_for_review(
                     "creative_changed_7d": creative_chg,
                     "stable_spend_days_30d": int(stable_days),
                     "bid_amount": round(bid_amount, 2),
-                    "bid_candidate": "bid_up" if bid_up_candidate else ("bid_down" if bid_down_candidate else None),
-                    "scale_up_candidate": scale_up_candidate,  # 新增:扩量候选标记
+                    # ★ 客观信号(替代旧 bid_candidate 预设答案)
+                    "roi_zone": (
+                        "below_pause_line" if (not pd.isna(dynamic_roi_7d) and dynamic_roi_7d < channel_roi_p50 * params["ROI_LOW_FACTOR"])
+                        else "bid_down_zone" if bid_down_candidate
+                        else "above_bid_up_line" if (not pd.isna(dynamic_roi_7d) and dynamic_roi_7d > channel_roi_p50 * params["BID_UP_ROI_FACTOR"])
+                        else "normal"
+                    ),
+                    "bid_up_candidate": bid_up_candidate,
+                    "fission_vs_tier": (
+                        "high" if (ad_fission is not None and tier_fission_mean is not None and ad_fission >= tier_fission_mean * 1.10)
+                        else "low" if (ad_fission is not None and tier_fission_mean is not None and ad_fission < tier_fission_mean * 0.90)
+                        else "normal" if (ad_fission is not None and tier_fission_mean is not None)
+                        else "unknown"
+                    ),
+                    "scale_up_candidate": scale_up_candidate,
                     # ===== 广告自身指标(供LLM对比同类基准) =====
                     "ad_fission": round(ad_fission, 4) if ad_fission is not None else None,
                     "ad_ctr": round(ad_ctr, 4) if ad_ctr is not None else None,

+ 4 - 3
examples/auto_put_ad_mini/tools/im_approval.py

@@ -527,9 +527,10 @@ async def send_approval_request(
             return ToolResult(title="send_approval_request", output="无决策数据")
 
         # 合并需审批的和无需操作的(供运营参考)
-        # ⚠️ 排除 hold(保持)和 observe(观察):这两类不需运营立刻干预,不写飞书表降低噪声
-        # creative_adjust(创意调整)仍保留——它是主动建议运营换素材的弱信号
-        FEISHU_EXCLUDE_ACTIONS = {"hold", "observe"}
+        # ⚠️ 排除不需运营立刻干预的 action,不写飞书表降低噪声
+        # - hold/observe:无需操作
+        # - scale_up:扩量逻辑尚在调试,暂不进审批表
+        FEISHU_EXCLUDE_ACTIONS = {"hold", "observe", "scale_up"}
         df_tier0_for_review = df_tier0
         if not df_tier0.empty:
             action_col = "final_action" if "final_action" in df_tier0.columns else "action"