Prompts updated. llm finetuned model used
Browse files- app/ads1/ads_analyst.py +2 -5
- app/ads1/budget_optimizer.py +1 -28
- app/ads1/growth_finder.py +1 -31
- app/ads1/keyword_inspector.py +2 -6
- app/ads1/prompt_templates.py +25 -0
- app/ads1/search_term_optimizer.py +2 -6
- app/models/llm.py +5 -2
app/ads1/ads_analyst.py
CHANGED
|
@@ -3,6 +3,7 @@ import json
|
|
| 3 |
import pandas as pd
|
| 4 |
|
| 5 |
from app.recs.generate import TARGET_CPL, generate_explanation, is_bad_llm_output
|
|
|
|
| 6 |
|
| 7 |
# -------------------------
|
| 8 |
# 1. DATA BUILDERS
|
|
@@ -133,11 +134,7 @@ def build_ads_analyst_context(dfs: dict, campaign_name: str | None = None) -> di
|
|
| 133 |
def build_ads_analyst_prompt(context: dict) -> str:
|
| 134 |
payload = json.dumps(context, indent=2, default=str)
|
| 135 |
name = context.get("campaign_name", "this campaign")
|
| 136 |
-
return (
|
| 137 |
-
f"Write 3 to 5 bullet points of actionable Google Ads insights for {name}.\n"
|
| 138 |
-
"Use simple language. One insight per bullet. Start each line with '- '. No intro sentence.\n\n"
|
| 139 |
-
f"Data (JSON):\n{payload}"
|
| 140 |
-
)
|
| 141 |
|
| 142 |
|
| 143 |
def rule_based_insights(context: dict) -> str:
|
|
|
|
| 3 |
import pandas as pd
|
| 4 |
|
| 5 |
from app.recs.generate import TARGET_CPL, generate_explanation, is_bad_llm_output
|
| 6 |
+
from app.ads1.prompt_templates import ads_analyst_prompt
|
| 7 |
|
| 8 |
# -------------------------
|
| 9 |
# 1. DATA BUILDERS
|
|
|
|
| 134 |
def build_ads_analyst_prompt(context: dict) -> str:
|
| 135 |
payload = json.dumps(context, indent=2, default=str)
|
| 136 |
name = context.get("campaign_name", "this campaign")
|
| 137 |
+
return ads_analyst_prompt(name, payload)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
|
| 139 |
|
| 140 |
def rule_based_insights(context: dict) -> str:
|
app/ads1/budget_optimizer.py
CHANGED
|
@@ -1,6 +1,4 @@
|
|
| 1 |
-
import json
|
| 2 |
import pandas as pd
|
| 3 |
-
from app.recs.generate import generate_explanation, is_bad_llm_output
|
| 4 |
|
| 5 |
def build_budget_features(df: pd.DataFrame) -> pd.DataFrame:
|
| 6 |
df = df.copy()
|
|
@@ -75,18 +73,6 @@ def build_budget_optimizer_context(dfs: dict, campaign_name: str | None = None):
|
|
| 75 |
"budget_actions": action_rows.round(2).to_dict("records")
|
| 76 |
}
|
| 77 |
|
| 78 |
-
def build_budget_optimizer_prompt(context: dict) -> str:
|
| 79 |
-
payload = json.dumps(context, indent=2, default=str)
|
| 80 |
-
name = context.get("campaign_name", "this account")
|
| 81 |
-
|
| 82 |
-
return (
|
| 83 |
-
f"Write 3 to 5 bullet points of actionable budget optimization insights for {name}.\n"
|
| 84 |
-
"Use the budget_actions list only. Each bullet must mention the campaign or keyword name, the budget action, and the evidence.\n"
|
| 85 |
-
"Use simple language. One self-contained budget action per bullet. Start each line with '- '. No intro sentence. Do not quote JSON values by themselves.\n\n"
|
| 86 |
-
f"Data (JSON):\n{payload}"
|
| 87 |
-
)
|
| 88 |
-
|
| 89 |
-
|
| 90 |
def rule_based_budget_actions(context: dict) -> str:
|
| 91 |
rows = context.get("budget_actions", [])
|
| 92 |
if not rows:
|
|
@@ -124,18 +110,5 @@ def run_budget_optimizer(dfs: dict, campaign_name: str | None = None) -> str:
|
|
| 124 |
return "⚠️ No campaign data available."
|
| 125 |
|
| 126 |
context = build_budget_optimizer_context(dfs, campaign_name)
|
| 127 |
-
|
| 128 |
print("🧠 [budget_optimizer] context built", flush=True)
|
| 129 |
-
|
| 130 |
-
prompt = build_budget_optimizer_prompt(context)
|
| 131 |
-
|
| 132 |
-
print("✍️ [budget_optimizer] prompt built", flush=True)
|
| 133 |
-
|
| 134 |
-
result = generate_explanation(prompt)
|
| 135 |
-
|
| 136 |
-
if is_bad_llm_output(result) or not result.strip().startswith("-") or result.count('"') >= 4:
|
| 137 |
-
print("⚠️ [budget_optimizer] fallback triggered", flush=True)
|
| 138 |
-
return rule_based_budget_actions(context)
|
| 139 |
-
|
| 140 |
-
print("📤 [budget_optimizer] result received", flush=True)
|
| 141 |
-
return result
|
|
|
|
|
|
|
| 1 |
import pandas as pd
|
|
|
|
| 2 |
|
| 3 |
def build_budget_features(df: pd.DataFrame) -> pd.DataFrame:
|
| 4 |
df = df.copy()
|
|
|
|
| 73 |
"budget_actions": action_rows.round(2).to_dict("records")
|
| 74 |
}
|
| 75 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 76 |
def rule_based_budget_actions(context: dict) -> str:
|
| 77 |
rows = context.get("budget_actions", [])
|
| 78 |
if not rows:
|
|
|
|
| 110 |
return "⚠️ No campaign data available."
|
| 111 |
|
| 112 |
context = build_budget_optimizer_context(dfs, campaign_name)
|
|
|
|
| 113 |
print("🧠 [budget_optimizer] context built", flush=True)
|
| 114 |
+
return rule_based_budget_actions(context)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/ads1/growth_finder.py
CHANGED
|
@@ -1,6 +1,4 @@
|
|
| 1 |
-
import json
|
| 2 |
import pandas as pd
|
| 3 |
-
from app.recs.generate import generate_explanation, is_bad_llm_output
|
| 4 |
|
| 5 |
def build_growth_finder_features(df: pd.DataFrame) -> pd.DataFrame:
|
| 6 |
df = df.copy()
|
|
@@ -60,19 +58,6 @@ def build_growth_finder_context(dfs: dict, campaign_name: str | None = None):
|
|
| 60 |
"growth_candidates": df[keep_cols].round(2).to_dict("records")
|
| 61 |
}
|
| 62 |
|
| 63 |
-
def build_growth_finder_prompt(context: dict) -> str:
|
| 64 |
-
payload = json.dumps(context, indent=2, default=str)
|
| 65 |
-
name = context.get("campaign_name", "this account")
|
| 66 |
-
|
| 67 |
-
return (
|
| 68 |
-
f"Write 3 to 5 bullet points of actionable growth opportunities for {name}.\n"
|
| 69 |
-
"Use the growth_candidates list only. Suggest ways to scale winners, expand related intent, or increase budget on efficient areas.\n"
|
| 70 |
-
"Each bullet must mention the keyword, the growth action, and the evidence. Do not list weak keywords or diagnose poor performance.\n"
|
| 71 |
-
"Use simple language. One self-contained growth opportunity per bullet. Start each line with '- '. No intro sentence. Do not quote JSON values by themselves.\n\n"
|
| 72 |
-
f"Data (JSON):\n{payload}"
|
| 73 |
-
)
|
| 74 |
-
|
| 75 |
-
|
| 76 |
def rule_based_growth_actions(context: dict) -> str:
|
| 77 |
rows = context.get("growth_candidates", [])
|
| 78 |
if not rows:
|
|
@@ -102,20 +87,5 @@ def run_growth_finder(dfs: dict, campaign_name: str | None = None) -> str:
|
|
| 102 |
return "⚠️ No keyword data available."
|
| 103 |
|
| 104 |
context = build_growth_finder_context(dfs, campaign_name)
|
| 105 |
-
if not context.get("growth_candidates"):
|
| 106 |
-
return rule_based_growth_actions(context)
|
| 107 |
-
|
| 108 |
print("🧠 [growth_finder] context built", flush=True)
|
| 109 |
-
|
| 110 |
-
prompt = build_growth_finder_prompt(context)
|
| 111 |
-
|
| 112 |
-
print("✍️ [growth_finder] prompt built", flush=True)
|
| 113 |
-
|
| 114 |
-
result = generate_explanation(prompt)
|
| 115 |
-
|
| 116 |
-
if is_bad_llm_output(result) or not result.strip().startswith("-") or result.count('"') >= 4:
|
| 117 |
-
print("⚠️ [growth_finder] fallback triggered", flush=True)
|
| 118 |
-
return rule_based_growth_actions(context)
|
| 119 |
-
|
| 120 |
-
print("📤 [growth_finder] result received", flush=True)
|
| 121 |
-
return result
|
|
|
|
|
|
|
| 1 |
import pandas as pd
|
|
|
|
| 2 |
|
| 3 |
def build_growth_finder_features(df: pd.DataFrame) -> pd.DataFrame:
|
| 4 |
df = df.copy()
|
|
|
|
| 58 |
"growth_candidates": df[keep_cols].round(2).to_dict("records")
|
| 59 |
}
|
| 60 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
def rule_based_growth_actions(context: dict) -> str:
|
| 62 |
rows = context.get("growth_candidates", [])
|
| 63 |
if not rows:
|
|
|
|
| 87 |
return "⚠️ No keyword data available."
|
| 88 |
|
| 89 |
context = build_growth_finder_context(dfs, campaign_name)
|
|
|
|
|
|
|
|
|
|
| 90 |
print("🧠 [growth_finder] context built", flush=True)
|
| 91 |
+
return rule_based_growth_actions(context)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/ads1/keyword_inspector.py
CHANGED
|
@@ -2,6 +2,7 @@ import json
|
|
| 2 |
import pandas as pd
|
| 3 |
|
| 4 |
from app.recs.generate import generate_explanation, is_bad_llm_output
|
|
|
|
| 5 |
|
| 6 |
|
| 7 |
# -------------------------
|
|
@@ -27,12 +28,7 @@ def build_keyword_features(df: pd.DataFrame) -> pd.DataFrame:
|
|
| 27 |
def build_keyword_prompt(context: dict) -> str:
|
| 28 |
payload = json.dumps(context, indent=2, default=str)
|
| 29 |
|
| 30 |
-
return (
|
| 31 |
-
"Write 3 to 5 bullet points of actionable keyword performance insights.\n"
|
| 32 |
-
"Classify individual keywords as winning, wasted spend, or scaling opportunities using CTR, cost, and conversions.\n"
|
| 33 |
-
"Use simple language. One insight per bullet. Start each line with '- '. No intro sentence.\n\n"
|
| 34 |
-
f"Data (JSON):\n{payload}"
|
| 35 |
-
)
|
| 36 |
# -------------------------
|
| 37 |
# Main runner
|
| 38 |
# -------------------------
|
|
|
|
| 2 |
import pandas as pd
|
| 3 |
|
| 4 |
from app.recs.generate import generate_explanation, is_bad_llm_output
|
| 5 |
+
from app.ads1.prompt_templates import keyword_inspector_prompt
|
| 6 |
|
| 7 |
|
| 8 |
# -------------------------
|
|
|
|
| 28 |
def build_keyword_prompt(context: dict) -> str:
|
| 29 |
payload = json.dumps(context, indent=2, default=str)
|
| 30 |
|
| 31 |
+
return keyword_inspector_prompt(payload)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
# -------------------------
|
| 33 |
# Main runner
|
| 34 |
# -------------------------
|
app/ads1/prompt_templates.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
def ads_analyst_prompt(name: str, payload: str) -> str:
|
| 2 |
+
return (
|
| 3 |
+
f"Write 3 to 5 bullet points of actionable Google Ads insights for {name}.\n"
|
| 4 |
+
"Use simple language. One insight per bullet. Start each line with '- '. No intro sentence.\n\n"
|
| 5 |
+
f"Data (JSON):\n{payload}"
|
| 6 |
+
)
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def keyword_inspector_prompt(payload: str) -> str:
|
| 10 |
+
return (
|
| 11 |
+
"Write 3 to 5 bullet points of actionable keyword performance insights.\n"
|
| 12 |
+
"Classify individual keywords as winning, wasted spend, or scaling opportunities using CTR, cost, and conversions.\n"
|
| 13 |
+
"Use simple language. One insight per bullet. Start each line with '- '. No intro sentence.\n\n"
|
| 14 |
+
f"Data (JSON):\n{payload}"
|
| 15 |
+
)
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def search_term_cleaner_prompt(name: str, payload: str) -> str:
|
| 19 |
+
return (
|
| 20 |
+
f"Write 3 to 5 bullet points of actionable search term cleanup insights for {name}.\n"
|
| 21 |
+
"Classify search terms as wasted spend, high intent, scale, or negative keyword candidates using total_cost, clicks, conversions, CPA, CVR, and CPC.\n"
|
| 22 |
+
"Use simple language. One search term action per bullet. Start each line with '- '. No intro sentence.\n\n"
|
| 23 |
+
f"Data (JSON):\n{payload}"
|
| 24 |
+
)
|
| 25 |
+
|
app/ads1/search_term_optimizer.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
import json
|
| 2 |
import pandas as pd
|
| 3 |
from app.recs.generate import generate_explanation, is_bad_llm_output
|
|
|
|
| 4 |
|
| 5 |
# -------------------------
|
| 6 |
# Feature engineering only
|
|
@@ -65,12 +66,7 @@ def build_search_optimizer_prompt(context: dict) -> str:
|
|
| 65 |
payload = json.dumps(context, indent=2, default=str)
|
| 66 |
name = context.get("campaign_name", "this campaign")
|
| 67 |
|
| 68 |
-
return (
|
| 69 |
-
f"Write 3 to 5 bullet points of actionable search term cleanup insights for {name}.\n"
|
| 70 |
-
"Use the search_terms list only. total_cost is total spend for that search term; cpc is cost per click.\n"
|
| 71 |
-
"Each bullet must mention the search term, the action, and the evidence. Use simple language. Start each line with '- '. No intro sentence.\n\n"
|
| 72 |
-
f"Data (JSON):\n{payload}"
|
| 73 |
-
)
|
| 74 |
|
| 75 |
|
| 76 |
# -------------------------
|
|
|
|
| 1 |
import json
|
| 2 |
import pandas as pd
|
| 3 |
from app.recs.generate import generate_explanation, is_bad_llm_output
|
| 4 |
+
from app.ads1.prompt_templates import search_term_cleaner_prompt
|
| 5 |
|
| 6 |
# -------------------------
|
| 7 |
# Feature engineering only
|
|
|
|
| 66 |
payload = json.dumps(context, indent=2, default=str)
|
| 67 |
name = context.get("campaign_name", "this campaign")
|
| 68 |
|
| 69 |
+
return search_term_cleaner_prompt(name, payload)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
|
| 71 |
|
| 72 |
# -------------------------
|
app/models/llm.py
CHANGED
|
@@ -6,8 +6,11 @@ from typing import Any
|
|
| 6 |
|
| 7 |
from huggingface_hub import hf_hub_download
|
| 8 |
|
| 9 |
-
HF_REPO = os.getenv("LLAMA_HF_REPO", "openbmb/MiniCPM5-1B-GGUF")
|
| 10 |
-
HF_FILENAME = os.getenv("LLAMA_HF_FILENAME", "MiniCPM5-1B-Q4_K_M.gguf")
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
_model: Any = None
|
| 13 |
_init_lock = threading.Lock()
|
|
|
|
| 6 |
|
| 7 |
from huggingface_hub import hf_hub_download
|
| 8 |
|
| 9 |
+
# HF_REPO = os.getenv("LLAMA_HF_REPO", "openbmb/MiniCPM5-1B-GGUF")
|
| 10 |
+
# HF_FILENAME = os.getenv("LLAMA_HF_FILENAME", "MiniCPM5-1B-Q4_K_M.gguf")
|
| 11 |
+
|
| 12 |
+
LLAMA_HF_REPO = os.getenv("LLAMA_HF_REPO", "ps1811/advisor-minicpm-finetuned-gguf")
|
| 13 |
+
LLAMA_HF_FILENAME= os.getenv("LLAMA_HF_FILENAME", "advisor-minicpm-q4_k_m.gguf")
|
| 14 |
|
| 15 |
_model: Any = None
|
| 16 |
_init_lock = threading.Lock()
|