Danialebrat commited on
Commit
87ddb27
·
1 Parent(s): 7447930

Modifying generator_agent.py to follow the examples and make the output as requested.

Browse files
ai_messaging_system_v2/Messaging_system/agents/generator_agent.py CHANGED
@@ -270,21 +270,17 @@ You should follow the instructions provided and use the examples (if available)
270
  {feedback_section}
271
  """
272
  elif has_examples:
273
- # Only examples, no instructions
274
  context = f"""
275
- Your task is to modify provided examples to output ONE personalized 'header' and 'message' as a push notification for a student learning {self.core_config.get_instrument()}.
276
 
277
- **IMPORTANT**: Carefully analyze the example messages provided below to understand our brand's:
278
- - Vocabulary, word choices, phrases
279
- - Writing style and tone for header and message
280
- - Voice and personality
281
- - Sentence structure and rhythm for header and message
282
- - Character, feel, and emojis
283
-
284
- Stick to the provided push notification examples. Select one 'header' and one 'message'. Modify the example slightly to make it more personalized giving the instructions and provide a modified personalized 'header' and 'message' as the output.
285
-
286
- **Character Limits**: header < {self.core_config.config_file["header_limit"]} and message < {self.core_config.config_file["message_limit"]} characters.
287
 
 
288
  {feedback_section}
289
  """
290
  else:
@@ -308,8 +304,8 @@ Your task is to generate a personalized 'header' and 'message' as a push notific
308
  # Both: examples demonstrate style/voice
309
  header = "### **Example Messages (demonstrating our brand's voice and style):**"
310
  elif has_examples and not has_instructions:
311
- # Only examples: analyze these to understand brand
312
- header = "### **Example Messages:**\nStudy these examples, and stick to the vocabulary and style. Select one of them, Modify the 'header' and 'message' slightly to make it more personalized giving the instructions and provide one modified personalized 'header' and 'message' as the output."
313
  else:
314
  header = "### **Example Messages:**"
315
 
@@ -394,39 +390,30 @@ User name is: {name} ;
394
  return ""
395
 
396
  instructions = f"""
397
- ### **Content Recommendation Guidelines:**
398
-
399
- Below is the content we want to recommend to the user:
400
 
401
  {recommendation_info}
402
 
403
- When incorporating this content:
404
- 1. **Title Usage**: Refer to the content naturally - paraphrase or describe it, don't quote verbatim. Avoid promotional tone.
405
- 2. **Content Type**: Mention the type (course, workout, etc.) only if it flows naturally.
406
- 3. **Artist/Instructor**: If the full name is available, mention it casually if appropriate. If only first name, do NOT include it. NEVER assume or hallucinate names.
407
- 4. **Tone**: Keep it light, supportive, engaging, and personal.
408
- 5. **Time Reference**: NEVER use time-related words ("new," "recent," "latest") or imply recency.
409
 
410
- Make the recommendation feel personalized and casually relevant, not generic or marketing-like.
 
 
 
 
411
  """
412
  return instructions.strip()
413
 
414
  def _get_output_instructions(self) -> str:
415
  """Get output format instructions."""
416
  instructions = f"""
417
- ### **Output Format:**
418
-
419
- Return a valid JSON object with this exact structure:
420
-
421
- {{
422
- "header": "your generated header",
423
- "message": "your generated message"
424
- }}
425
-
426
- **Remember**:
427
- - header < {self.core_config.config_file["header_limit"]} characters
428
- - message < {self.core_config.config_file["message_limit"]} characters
429
- - Valid JSON format
430
  """
431
  return instructions.strip()
432
 
@@ -449,13 +436,12 @@ You are an expert at creating personalized, engaging push notifications for {sel
449
 
450
  **Critical Rules:**
451
  - NEVER use time-related words ("new," "recent," "latest," etc.) or imply recency
 
452
  - Stay within character limits: header < {self.core_config.config_file["header_limit"]}, message < {self.core_config.config_file["message_limit"]}
453
  - Return valid JSON with "header" and "message" keys
454
 
455
- **Avoid these phrases and similar variations:**
456
  {banned_phrases}
457
-
458
- **Also avoid these phrases:**
459
  {jargon_list}
460
 
461
  **Tone**: Supportive, engaging, personal - never pushy or promotional.
 
270
  {feedback_section}
271
  """
272
  elif has_examples:
273
+ # Only examples, no instructions - use strict selection approach
274
  context = f"""
275
+ **Task**: Select ONE header and ONE message from the options below for a {self.core_config.get_instrument()} student.
276
 
277
+ **Rules**:
278
+ 1. SELECT from examples - do NOT create new content
279
+ 2. PRESERVE all emojis exactly as shown
280
+ 3. Personalize ONLY by: provided recommendation, adding user's name or using their other personalized info.
281
+ 4. Keep the EXACT tone, style, and sentence structure
 
 
 
 
 
282
 
283
+ **Limits**: header < {self.core_config.config_file["header_limit"]}, message < {self.core_config.config_file["message_limit"]} chars
284
  {feedback_section}
285
  """
286
  else:
 
304
  # Both: examples demonstrate style/voice
305
  header = "### **Example Messages (demonstrating our brand's voice and style):**"
306
  elif has_examples and not has_instructions:
307
+ # Only examples: frame as available options for strict selection
308
+ header = "### **Available Options (select one header + one message):**"
309
  else:
310
  header = "### **Example Messages:**"
311
 
 
390
  return ""
391
 
392
  instructions = f"""
393
+ ### **Content Recommendation:**
 
 
394
 
395
  {recommendation_info}
396
 
397
+ **CRITICAL - Articulate Value**: Don't just mention the content. Explain WHY it matters:
398
+ - What skill will they develop?
399
+ - What will they be able to DO after taking it?
400
+
401
+ BAD: "Try the Fills workout - it's great for technique"
402
+ GOOD: "Master those tricky fills that make songs come alive"
403
 
404
+ **Rules**:
405
+ 1. Paraphrase content naturally - never quote verbatim
406
+ 2. Create desire to click - focus on benefit, not feature
407
+ 3. NEVER use time words (new, recent, latest)
408
+ 4. If artist full name available, mention casually. If only first name, omit.
409
  """
410
  return instructions.strip()
411
 
412
  def _get_output_instructions(self) -> str:
413
  """Get output format instructions."""
414
  instructions = f"""
415
+ ### **Output (JSON)**:
416
+ {{"header": "<{self.core_config.config_file["header_limit"]} chars", "message": "<{self.core_config.config_file["message_limit"]} chars"}}
 
 
 
 
 
 
 
 
 
 
 
417
  """
418
  return instructions.strip()
419
 
 
436
 
437
  **Critical Rules:**
438
  - NEVER use time-related words ("new," "recent," "latest," etc.) or imply recency
439
+ - PRESERVE all emojis from selected examples - copy them exactly
440
  - Stay within character limits: header < {self.core_config.config_file["header_limit"]}, message < {self.core_config.config_file["message_limit"]}
441
  - Return valid JSON with "header" and "message" keys
442
 
443
+ **Avoid these phrases:**
444
  {banned_phrases}
 
 
445
  {jargon_list}
446
 
447
  **Tone**: Supportive, engaging, personal - never pushy or promotional.
ai_messaging_system_v2/Previous_codes/PromptGenerator.py ADDED
@@ -0,0 +1,288 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ THis class generate proper prompts for the messaging system
3
+ """
4
+ import pandas as pd
5
+ from tqdm import tqdm
6
+
7
+
8
+ class PromptGenerator:
9
+
10
+ def __init__(self, Core):
11
+ self.Core = Core
12
+
13
+ # --------------------------------------------------------------
14
+ # --------------------------------------------------------------
15
+ def generate_prompts(self, stage=1):
16
+ """
17
+ generates a personalized message for each student
18
+ :return:
19
+ """
20
+
21
+ # engine = PromptEngine(self.Core)
22
+ self.Core.users_df = self.Core.users_df.drop_duplicates(subset=['user_id'])
23
+
24
+ # if we have personalized information about them, we generate a personalized prompt
25
+ for idx, row in tqdm(self.Core.users_df.iterrows(), desc="generating prompts"):
26
+ # check if we have enough information to generate a personalized message
27
+ if stage == 1:
28
+ prompt = self.generate_personalized_prompt(user=row)
29
+ else:
30
+ prompt = self.generate_follow_up_prompt(user=row)
31
+
32
+ self.Core.users_df.at[idx, "prompt"] = prompt
33
+ self.Core.users_df.at[idx, "source"] = "AI-generated"
34
+
35
+ return self.Core
36
+
37
+
38
+ # --------------------------------------------------------------
39
+ def safe_get(self, value):
40
+ return str(value) if pd.notna(value) else "Not available"
41
+
42
+ # ==============================================================
43
+ def get_user_profile(self, user):
44
+ """
45
+ getting personalized information about users (e.g. preferences)
46
+ :param user:
47
+ :return:
48
+ """
49
+ if self.Core.personalization:
50
+ user_info = f"""
51
+
52
+ ### **Use below information from the user to **modify** the selected header and message (without any change in style, tune and content) and make the final output more personalized if applicable.
53
+ Use these only to *flavour* the existing header and message. Never add new sentences just to stuff profile data.
54
+
55
+ - The user is a {str(self.Core.get_instrument())} student.
56
+ - {self.safe_get(self.Core.segment_info)}
57
+ - User profile --> Only use **indirectly** if it can improve personalization (Don't use their preferred genre or styles directly in the message:
58
+ {self.safe_get(user.get("user_info"))}
59
+ """
60
+ # birth_day_instructions = self.birth_day_instructions(user)
61
+ # if birth_day_instructions is not None:
62
+ # user_info += "\n" + birth_day_instructions
63
+ ## eliminate:
64
+ # - first name: {self.safe_get(user.get("first_name"))} --> Only use if is available and the first name is a **valid name**
65
+
66
+ else:
67
+
68
+ user_info = f"""
69
+ ### **Use below Information to select the best header and message from available options:**
70
+ Do not **change** or **modify** or **add to** the selected option and **use the original selected header and message without ANY change**
71
+
72
+ Here is the information about the user:
73
+ - The user is a {str(self.Core.get_instrument())} student.
74
+ - {self.safe_get(self.Core.segment_info)}
75
+ - User_profile:
76
+ {self.safe_get(user.get("user_info"))}
77
+ """
78
+ return user_info
79
+
80
+ ## deleted from profile
81
+ # first name: {self.safe_get(user.get("first_name"))}
82
+ # Weeks since Last interaction:{self.safe_get(user.get("weeks_since_last_interaction"))}
83
+ # {self.safe_get(additional_info)}
84
+ # ** User
85
+ # profile: **
86
+ #
87
+ # {self.safe_get(user.get("user_info"))}
88
+
89
+ return user_info
90
+
91
+ # --------------------------------------------------------------
92
+ def birth_day_instructions(self, user):
93
+ # Birthday reminder
94
+ if pd.notna(user["birthday_reminder"]) and user["birthday_reminder"] not in [None, [], {}]:
95
+ instructions = f"""
96
+ - **Include a short message to remind them that their birthday is coming up.**: {str(user["birthday_reminder"])} Days until their birthday.
97
+ """
98
+ return instructions
99
+ else:
100
+ return None
101
+
102
+ def generate_personalized_prompt(self, user):
103
+ """
104
+ generate a personalized prompt by putting the information from the user into a template prompt
105
+ :return: Personalized prompt (string)
106
+ """
107
+ input_context = self.input_context()
108
+ user_info = self.get_user_profile(user=user)
109
+
110
+ recommendation_instructions = self.recommendations_instructions(user)
111
+
112
+ example_output = self.example_output()
113
+
114
+ output_instructions = self.output_instruction()
115
+
116
+ prompt = f"""
117
+ {input_context}
118
+
119
+ {example_output}
120
+
121
+ {user_info}
122
+
123
+ {recommendation_instructions}
124
+
125
+ {output_instructions}
126
+ """
127
+
128
+ return prompt
129
+
130
+ # --------------------------------------------------------------
131
+ # --------------------------------------------------------------
132
+ def input_context(self):
133
+ """
134
+ :return: input instructions as a string
135
+ """
136
+
137
+ if self.Core.personalization:
138
+ context = f"""
139
+ Your task is to select the best 'header' and a 'message' for a {self.Core.get_instrument()} student as a push notification.
140
+ Based on the user instructions, you might need to **modify the selected option** very minimal and slightly to improve personalization if capable.
141
+ **Important Note**: header < {self.Core.config_file["header_limit"]} and message < {self.Core.config_file["message_limit"]} characters.
142
+ """
143
+
144
+ else:
145
+ context = f"""
146
+ Your task is to select the best 'header' and a 'message' for a {self.Core.get_instrument()} student as a push notification.
147
+ DO NOT **change** or **modify** or **add to** the selected option in any shape or form. **Use the exact original selected header and message without ANY change**
148
+ """
149
+
150
+ return context
151
+
152
+
153
+ # --------------------------------------------------------------
154
+ # --------------------------------------------------------------
155
+ def recommendations_instructions(self, user):
156
+ """
157
+ instructions about target recommendation for the user
158
+ :param user:
159
+ :return:
160
+ """
161
+
162
+
163
+ instructions_for_recsys = f"""
164
+ ### ** Recommendation Personalization Guidelines **
165
+
166
+ Below is the content we want to recommend to the user:
167
+
168
+ → Recommended Content Details:
169
+ {user["recommendation_info"]}
170
+
171
+ When incorporating this content into the message and header, follow these guidelines to keep the header and message friendly, relevant, and casual (not too scripted):
172
+
173
+ 1. **Title Usage**:
174
+ - Refer to the **CONTENT_TITLE** or content details naturally in the message — paraphrase or describe it, but do *not* quote it or use it verbatim.
175
+ - Avoid making it feel like a promotion; frame it as something that *might interest* or *help* the user.
176
+
177
+ 2. **Content Type Context**:
178
+ - Mention the **CONTENT_TYPE** (e.g., course, workout) only if it flows naturally in the message.
179
+
180
+
181
+ 3. **Artist/Instructor Name**:
182
+ - If the full name of the **ARTIST** is available, mention it casually if appropriate (e.g., "led by Jordan Mitchell").
183
+ - If only the first name is known, do *not* include it in the message at all.
184
+ - **DO NOT ASSUME or HALLUCINATE Artist name based on previous messages**, only refer to Recommended Content Details provided.
185
+
186
+ 4. **Tone & Style**:
187
+ - Keep the tone light, supportive, and personal — like a helpful suggestion from a friend.
188
+ - Avoid sounding pushy, overly promotional, or marketing pitch.
189
+
190
+ 5. **Flexibility**:
191
+ - You don’t need to include all elements every time. Prioritize what feels most relevant and natural based on the context.
192
+
193
+ 6. **Time Reference**:
194
+ - NEVER use time-related words (“new,” “recent,” “latest,” etc.) and Never imply recency of the content in any way.
195
+
196
+ Goal: Make the recommendation feel personalized and casually relevant — not generic or copy-pasted.
197
+ """
198
+
199
+ if self.Core.personalization and self.Core.involve_recsys_result:
200
+ return instructions_for_recsys
201
+ else:
202
+ return ""
203
+
204
+ # --------------------------------------------------------------
205
+ # --------------------------------------------------------------
206
+ def output_instruction(self):
207
+ """
208
+ :return: output instructions as a string
209
+ """
210
+
211
+ general_instructions = f"""
212
+ - Ensure that the output is a valid JSON following above structure.
213
+ """
214
+
215
+ instructions = f"""
216
+ ### **Output instructions**:
217
+ - header < {self.Core.config_file["header_limit"]} and message < {self.Core.config_file["message_limit"]} characters.
218
+
219
+ **Expected output structure:**
220
+
221
+ {{
222
+ "header": "final header considering instructions",
223
+ "message": "final message considering instructions",
224
+ }}
225
+
226
+ {general_instructions}
227
+ """
228
+
229
+ return instructions
230
+
231
+ # --------------------------------------------------------------
232
+ # --------------------------------------------------------------
233
+ def example_output(self):
234
+ """
235
+ returns an example output (1-shot) to guide the LLM
236
+ :return: example output
237
+ """
238
+
239
+ if self.Core.sample_example is None:
240
+
241
+ return ""
242
+
243
+ else:
244
+ # one shot prompting
245
+ example = f"""
246
+ Below are the available options to select the header and message for the push notification:
247
+
248
+ ### **Available options:**
249
+ {self.Core.sample_example}
250
+ """
251
+
252
+ return example
253
+
254
+ # =============================================================
255
+ def generate_follow_up_prompt(self, user):
256
+ """
257
+ Creates a prompt to feed to the LLM, incorporating 3 previously generated messages.
258
+
259
+ :param previous_messages: A list of dicts, each containing 'header' and 'message'.
260
+ :return: A user-facing prompt string instructing the model to produce a new message.
261
+ """
262
+ previous_text_str = str(user["previous_messages"])
263
+ user_info = self.get_user_profile(user=user)
264
+ input_context = self.input_context()
265
+ recommendation_instructions = self.recommendations_instructions(user)
266
+ output_instructions = self.output_instruction()
267
+ examples = self.example_output()
268
+
269
+ # Craft the prompt
270
+ prompt = f"""
271
+ We have previously sent these push notifications to the user and The user has not re-engaged yet:
272
+
273
+ ** Previous messages **
274
+ {previous_text_str}
275
+
276
+ {input_context}
277
+ - The new selection should be different from previous headers and messages and we should not have similar words and phrases from previous sends.
278
+
279
+ {examples}
280
+
281
+ {user_info}
282
+
283
+ {recommendation_instructions}
284
+
285
+ {output_instructions}
286
+ """
287
+
288
+ return prompt