-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathemail_ape_loop.py
More file actions
133 lines (95 loc) · 3.02 KB
/
email_ape_loop.py
File metadata and controls
133 lines (95 loc) · 3.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import os
import json
from openai import OpenAI
from dotenv import load_dotenv
from evaluators.email_evaluator import evaluate_email
load_dotenv()
# --- Config ---
MODEL = "gpt-4o-mini" # cheap
N_GENERATIONS = 2
N_MUTATIONS = 5
TOP_K = 2
OUTPUT_FILE = "email_best_prompt.json"
LOG_FILE = "email_ape_log.json"
SEED_PROMPT = "Write a professional email clearly and politely."
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
# --- Email Generator ---
def generate_email(prompt: str, task: str) -> str:
response = client.chat.completions.create(
model=MODEL,
temperature=0.7,
max_tokens=150,
messages=[
{"role": "system", "content": prompt},
{"role": "user", "content": task}
],
)
return response.choices[0].message.content.strip()
# --- Fitness Function ---
def evaluate_prompt(prompt: str, dataset: list) -> float:
scores = []
for item in dataset:
email = generate_email(prompt, item["task"])
eval_result = evaluate_email(email)
score = eval_result.get("score", 0.0)
if len(email) > 200:
score -= 0.1
# clamp score between 0 and 1
score = max(0, min(score, 1))
scores.append(score)
return sum(scores) / len(scores)
# --- Prompt Mutator ---
MUTATOR_SYSTEM = """
You improve prompts for email writing.
Given a prompt, generate a better version.
Keep it short and clear.
""".strip()
def mutate_prompt(prompt: str) -> str:
response = client.chat.completions.create(
model=MODEL,
temperature=0.7,
max_tokens=60,
messages=[
{"role": "system", "content": MUTATOR_SYSTEM},
{"role": "user", "content": prompt}
],
)
return response.choices[0].message.content.strip()
# --- APE Loop ---
def run_ape():
with open("JSON/email_dataset.json") as f:
dataset = json.load(f)
population = [SEED_PROMPT]
log = []
for gen in range(N_GENERATIONS):
print(f"\n=== Generation {gen} ===")
scored = []
for prompt in population:
score = evaluate_prompt(prompt, dataset)
scored.append((prompt, score))
print(f"Score: {score:.3f}")
print(f"Prompt: {prompt}\n")
# Sort by score
scored.sort(key=lambda x: x[1], reverse=True)
# Keep top K
top_prompts = [p for p, _ in scored[:TOP_K]]
# Log
log.append({
"generation": gen,
"top_score": scored[0][1],
"best_prompt": scored[0][0]
})
# Mutate
new_population = top_prompts.copy()
for p in top_prompts:
for _ in range(N_MUTATIONS):
new_population.append(mutate_prompt(p))
population = new_population
# Best prompt
best_prompt = scored[0][0]
with open(OUTPUT_FILE, "w") as f:
json.dump({"best_prompt": best_prompt}, f, indent=2)
with open(LOG_FILE, "w") as f:
json.dump(log, f, indent=2)
if __name__ == "__main__":
run_ape()