Commit ·
fa44311
1
Parent(s): 4f26c60
-changing subsequent messages similar to initial messages and change prompting
Browse files
.idea/AI_Message_Generator.iml
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
+
<module type="PYTHON_MODULE" version="4">
|
| 3 |
+
<component name="NewModuleRootManager">
|
| 4 |
+
<content url="file://$MODULE_DIR$" />
|
| 5 |
+
<orderEntry type="jdk" jdkName="Python 3.9" jdkType="Python SDK" />
|
| 6 |
+
<orderEntry type="sourceFolder" forTests="false" />
|
| 7 |
+
</component>
|
| 8 |
+
</module>
|
Messaging_system/MultiMessage.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
import json
|
| 2 |
import time
|
| 3 |
from openai import OpenAI
|
| 4 |
-
|
| 5 |
from Messaging_system.PromptEng import PromptEngine
|
| 6 |
from Messaging_system.protection_layer import ProtectionLayer
|
| 7 |
import openai
|
|
@@ -16,6 +16,7 @@ class MultiMessage:
|
|
| 16 |
self.Core = CoreConfig
|
| 17 |
self.llm = LLM(CoreConfig)
|
| 18 |
self.engine = PromptEngine(self.Core)
|
|
|
|
| 19 |
|
| 20 |
# --------------------------------------------------------------
|
| 21 |
def generate_multi_messages(self, user):
|
|
@@ -56,7 +57,7 @@ class MultiMessage:
|
|
| 56 |
# Already have the first message, so generate the next (n-1) messages
|
| 57 |
for step in range(1, total_to_generate + 1):
|
| 58 |
# 2) Generate the next message referencing all so-far messages
|
| 59 |
-
next_msg_raw = self.generate_next_messages(message_sequence, step+1)
|
| 60 |
if not next_msg_raw:
|
| 61 |
print(f"Could not generate the message for step {step}. Stopping.")
|
| 62 |
break
|
|
@@ -93,7 +94,7 @@ class MultiMessage:
|
|
| 93 |
return json.dumps(final_structure, ensure_ascii=False)
|
| 94 |
|
| 95 |
# --------------------------------------------------------------
|
| 96 |
-
def generate_next_messages(self, previous_messages, step):
|
| 97 |
"""
|
| 98 |
Uses only the last two previously generated messages to produce the next message.
|
| 99 |
Returns a *raw* dictionary (header, message, etc.) from the LLM.
|
|
@@ -109,7 +110,7 @@ class MultiMessage:
|
|
| 109 |
context = previous_messages
|
| 110 |
|
| 111 |
# 1) Build a prompt that includes only those last two messages
|
| 112 |
-
prompt = self.generate_prompt(context, step)
|
| 113 |
|
| 114 |
# new_prompt = self.engine.prompt_engineering(prompt)
|
| 115 |
|
|
@@ -128,29 +129,28 @@ class MultiMessage:
|
|
| 128 |
if self.Core.subsequent_examples is not None:
|
| 129 |
|
| 130 |
instructions = f"""
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
return instructions
|
| 141 |
else:
|
| 142 |
return ""
|
| 143 |
|
| 144 |
# --------------------------------------------------------------
|
| 145 |
-
def generate_prompt(self, previous_messages, step):
|
| 146 |
"""
|
| 147 |
Creates a prompt to feed to the LLM, incorporating 3 previously generated messages.
|
| 148 |
|
| 149 |
:param previous_messages: A list of dicts, each containing 'header' and 'message'.
|
| 150 |
:return: A user-facing prompt string instructing the model to produce a new message.
|
| 151 |
"""
|
| 152 |
-
# Build a textual summary of previous messages
|
| 153 |
-
# ──► NEW: keep at most the last three messages
|
| 154 |
recent_messages = previous_messages[-3:]
|
| 155 |
|
| 156 |
previous_text = []
|
|
@@ -162,10 +162,10 @@ class MultiMessage:
|
|
| 162 |
# Combine into a single string
|
| 163 |
previous_text_str = "\n\n".join(previous_text)
|
| 164 |
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
|
| 170 |
examples = self.get_examples(step)
|
| 171 |
|
|
@@ -173,48 +173,74 @@ class MultiMessage:
|
|
| 173 |
# Craft the prompt
|
| 174 |
prompt = f"""
|
| 175 |
We have previously sent these push notifications to the user and The user has not re-engaged yet:
|
| 176 |
-
User info:
|
| 177 |
-
{self.Core.segment_info}
|
| 178 |
|
| 179 |
** Previous messages **
|
| 180 |
{previous_text_str}
|
| 181 |
|
|
|
|
| 182 |
**Objective**
|
| 183 |
-
Write the *next* follow up personalized push notification following the instructions
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
# **General
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
- "message" and "header" **MUST NOT** contain similar or close words and phrases, and make sure there is no grammar problem.
|
| 194 |
-
- New header and message **MUST BE** clearly different from all earlier headers & messages in terms of vocabulary and phrases (They must not be similar)
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
# **Tune, Style and specific instructions**:
|
| 198 |
-
- The message and header should sound natural and friendly, like something that people would normally say in daily conversations. This is the most important part of the notification. It should sound like everyday natural speech: friendly conversation.
|
| 199 |
- {self.Core.subsequence_messages[step]}
|
| 200 |
|
| 201 |
{examples}
|
| 202 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 203 |
|
| 204 |
-
|
| 205 |
-
- "header" must be
|
| 206 |
-
- "message" must be
|
| 207 |
-
-
|
|
|
|
|
|
|
| 208 |
|
|
|
|
|
|
|
| 209 |
|
| 210 |
-
Return only JSON of the form:
|
| 211 |
{{
|
| 212 |
-
"header": "
|
| 213 |
-
"message": "
|
| 214 |
}}
|
| 215 |
-
""".strip()
|
| 216 |
|
| 217 |
-
|
|
|
|
|
|
|
| 218 |
|
| 219 |
# --------------------------------------------------------------
|
| 220 |
def parsing_output_message(self, message, user):
|
|
|
|
| 1 |
import json
|
| 2 |
import time
|
| 3 |
from openai import OpenAI
|
| 4 |
+
from Messaging_system.PromptGenerator import PromptGenerator
|
| 5 |
from Messaging_system.PromptEng import PromptEngine
|
| 6 |
from Messaging_system.protection_layer import ProtectionLayer
|
| 7 |
import openai
|
|
|
|
| 16 |
self.Core = CoreConfig
|
| 17 |
self.llm = LLM(CoreConfig)
|
| 18 |
self.engine = PromptEngine(self.Core)
|
| 19 |
+
self.promptGen = PromptGenerator(self.Core)
|
| 20 |
|
| 21 |
# --------------------------------------------------------------
|
| 22 |
def generate_multi_messages(self, user):
|
|
|
|
| 57 |
# Already have the first message, so generate the next (n-1) messages
|
| 58 |
for step in range(1, total_to_generate + 1):
|
| 59 |
# 2) Generate the next message referencing all so-far messages
|
| 60 |
+
next_msg_raw = self.generate_next_messages(message_sequence, step+1, user)
|
| 61 |
if not next_msg_raw:
|
| 62 |
print(f"Could not generate the message for step {step}. Stopping.")
|
| 63 |
break
|
|
|
|
| 94 |
return json.dumps(final_structure, ensure_ascii=False)
|
| 95 |
|
| 96 |
# --------------------------------------------------------------
|
| 97 |
+
def generate_next_messages(self, previous_messages, step, user):
|
| 98 |
"""
|
| 99 |
Uses only the last two previously generated messages to produce the next message.
|
| 100 |
Returns a *raw* dictionary (header, message, etc.) from the LLM.
|
|
|
|
| 110 |
context = previous_messages
|
| 111 |
|
| 112 |
# 1) Build a prompt that includes only those last two messages
|
| 113 |
+
prompt = self.generate_prompt(context, step, user)
|
| 114 |
|
| 115 |
# new_prompt = self.engine.prompt_engineering(prompt)
|
| 116 |
|
|
|
|
| 129 |
if self.Core.subsequent_examples is not None:
|
| 130 |
|
| 131 |
instructions = f"""
|
| 132 |
+
# ** Example **
|
| 133 |
+
Below are some acceptable examples of the voice we want. Create a header and message that follow the same style, tone, vocabulary, and characteristics.
|
| 134 |
+
Mimic the example style as much as possible and make it personalized using provided information.
|
| 135 |
+
|
| 136 |
|
| 137 |
+
### **Good Examples:**
|
| 138 |
+
|
| 139 |
+
{self.Core.subsequent_examples[step]}
|
| 140 |
+
"""
|
| 141 |
return instructions
|
| 142 |
else:
|
| 143 |
return ""
|
| 144 |
|
| 145 |
# --------------------------------------------------------------
|
| 146 |
+
def generate_prompt(self, previous_messages, step, user):
|
| 147 |
"""
|
| 148 |
Creates a prompt to feed to the LLM, incorporating 3 previously generated messages.
|
| 149 |
|
| 150 |
:param previous_messages: A list of dicts, each containing 'header' and 'message'.
|
| 151 |
:return: A user-facing prompt string instructing the model to produce a new message.
|
| 152 |
"""
|
| 153 |
+
# Build a textual summary of previous messages - last three
|
|
|
|
| 154 |
recent_messages = previous_messages[-3:]
|
| 155 |
|
| 156 |
previous_text = []
|
|
|
|
| 162 |
# Combine into a single string
|
| 163 |
previous_text_str = "\n\n".join(previous_text)
|
| 164 |
|
| 165 |
+
user_info = self.promptGen.get_user_profile(user=user)
|
| 166 |
+
input_context = self.promptGen.input_context()
|
| 167 |
+
output_instructions = self.output_instruction()
|
| 168 |
+
general_specifications = self.general_specifications()
|
| 169 |
|
| 170 |
examples = self.get_examples(step)
|
| 171 |
|
|
|
|
| 173 |
# Craft the prompt
|
| 174 |
prompt = f"""
|
| 175 |
We have previously sent these push notifications to the user and The user has not re-engaged yet:
|
|
|
|
|
|
|
| 176 |
|
| 177 |
** Previous messages **
|
| 178 |
{previous_text_str}
|
| 179 |
|
| 180 |
+
|
| 181 |
**Objective**
|
| 182 |
+
Write the *next* follow up personalized push notification following the instructions and what we know about the user.
|
| 183 |
+
{input_context}
|
| 184 |
+
|
| 185 |
+
{user_info}
|
| 186 |
+
|
| 187 |
+
### ** General Specifications: **
|
| 188 |
+
|
| 189 |
+
{general_specifications}
|
| 190 |
+
|
| 191 |
+
# **specific instructions**:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 192 |
- {self.Core.subsequence_messages[step]}
|
| 193 |
|
| 194 |
{examples}
|
| 195 |
|
| 196 |
+
{output_instructions}
|
| 197 |
+
"""
|
| 198 |
+
|
| 199 |
+
return prompt
|
| 200 |
+
# ===========================================================================
|
| 201 |
+
def general_specifications(self):
|
| 202 |
+
"""
|
| 203 |
+
general_specifications
|
| 204 |
+
:return: instructions
|
| 205 |
+
"""
|
| 206 |
+
|
| 207 |
+
instructions = """
|
| 208 |
+
- Start directly with the message content without greetings or closing phrases.
|
| 209 |
+
- Avoid using same or similar words so close together in "message" and "header", and make sure there is no grammar problem.
|
| 210 |
+
- The message, vocabulary and sentences **MUST** sound like a natural conversation: something that people normally say in daily conversations.
|
| 211 |
+
|
| 212 |
+
"""
|
| 213 |
+
return instructions
|
| 214 |
+
|
| 215 |
+
# =============================================================================
|
| 216 |
+
|
| 217 |
+
def output_instruction(self):
|
| 218 |
+
"""
|
| 219 |
+
:return: output instructions as a string
|
| 220 |
+
"""
|
| 221 |
+
|
| 222 |
+
# Provide constraints for our next push notification
|
| 223 |
+
header_limit = self.Core.config_file.get("header_limit", 50)
|
| 224 |
+
message_limit = self.Core.config_file.get("message_limit", 200)
|
| 225 |
|
| 226 |
+
general_instructions = f"""
|
| 227 |
+
- The "header" must be less than {header_limit} character.
|
| 228 |
+
- The "message" must be less than {message_limit} character.
|
| 229 |
+
- Preserve special characters and emojis in the message, you are **ONLY ALLOWED**allowed to use {self.Core.get_emoji()} emoji if needed, ONLY ONCE, and ONLY at the end of header or message).
|
| 230 |
+
- Ensure that the output is a valid JSON and not include any text outside the JSON code block.
|
| 231 |
+
"""
|
| 232 |
|
| 233 |
+
instructions = f"""
|
| 234 |
+
Expected output structure:
|
| 235 |
|
|
|
|
| 236 |
{{
|
| 237 |
+
"header": "Generated title",
|
| 238 |
+
"message": "Generated message",
|
| 239 |
}}
|
|
|
|
| 240 |
|
| 241 |
+
{general_instructions}
|
| 242 |
+
"""
|
| 243 |
+
return instructions
|
| 244 |
|
| 245 |
# --------------------------------------------------------------
|
| 246 |
def parsing_output_message(self, message, user):
|
Messaging_system/PromptGenerator.py
CHANGED
|
@@ -43,12 +43,16 @@ class PromptGenerator:
|
|
| 43 |
# additional_info = self.user_additional_info(user)
|
| 44 |
|
| 45 |
user_info = f"""
|
| 46 |
-
|
| 47 |
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
|
| 53 |
"""
|
| 54 |
|
|
@@ -94,15 +98,15 @@ class PromptGenerator:
|
|
| 94 |
# eliminate {task_instructions} and {recommendations_instructions}
|
| 95 |
|
| 96 |
prompt = f"""
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
"""
|
| 107 |
|
| 108 |
return prompt
|
|
@@ -115,7 +119,12 @@ class PromptGenerator:
|
|
| 115 |
"""
|
| 116 |
|
| 117 |
context = f"""
|
| 118 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
"""
|
| 120 |
|
| 121 |
return context
|
|
@@ -130,9 +139,9 @@ class PromptGenerator:
|
|
| 130 |
|
| 131 |
instructions = f"""
|
| 132 |
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
"""
|
| 137 |
|
| 138 |
return instructions
|
|
@@ -292,12 +301,12 @@ class PromptGenerator:
|
|
| 292 |
|
| 293 |
if self.Core.platform == "push":
|
| 294 |
instructions = f"""
|
| 295 |
-
|
| 296 |
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
|
| 302 |
"""
|
| 303 |
|
|
@@ -345,28 +354,28 @@ class PromptGenerator:
|
|
| 345 |
|
| 346 |
example_output = self.example_output()
|
| 347 |
general_instructions = f"""
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
|
| 352 |
"""
|
| 353 |
|
| 354 |
instructions = f"""
|
| 355 |
-
|
| 356 |
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
| 361 |
|
| 362 |
-
|
| 363 |
"""
|
| 364 |
|
| 365 |
output_instructions = f"""
|
| 366 |
-
|
| 367 |
|
| 368 |
-
|
| 369 |
-
|
| 370 |
"""
|
| 371 |
|
| 372 |
return output_instructions
|
|
|
|
| 43 |
# additional_info = self.user_additional_info(user)
|
| 44 |
|
| 45 |
user_info = f"""
|
| 46 |
+
### **User Information:**
|
| 47 |
|
| 48 |
+
Here is the information about the user:
|
| 49 |
+
- The user is a {str(self.Core.get_instrument())} student.
|
| 50 |
+
- {self.safe_get(self.Core.segment_info)}
|
| 51 |
+
- first name: {self.safe_get(user.get("first_name"))}
|
| 52 |
+
|
| 53 |
+
**user_profile**
|
| 54 |
+
|
| 55 |
+
{self.safe_get(user.get("user_info"))}
|
| 56 |
|
| 57 |
"""
|
| 58 |
|
|
|
|
| 98 |
# eliminate {task_instructions} and {recommendations_instructions}
|
| 99 |
|
| 100 |
prompt = f"""
|
| 101 |
+
{input_context}
|
| 102 |
+
|
| 103 |
+
{user_info}
|
| 104 |
+
|
| 105 |
+
{cta}
|
| 106 |
+
|
| 107 |
+
{general_instructions}
|
| 108 |
+
|
| 109 |
+
{output_instructions}
|
| 110 |
"""
|
| 111 |
|
| 112 |
return prompt
|
|
|
|
| 119 |
"""
|
| 120 |
|
| 121 |
context = f"""
|
| 122 |
+
Your task is to write a 'header' and a 'message' as a push notification for a {self.Core.get_instrument()} student that sounds like everyday natural speech: friendly, short, no jargon by following the instructions.
|
| 123 |
+
The output should sound like something that a {self.Core.get_instrument()} instructor would realistically say to a student in a daily conversation.
|
| 124 |
+
|
| 125 |
+
** Examples of actual phrases an instructor might say:**
|
| 126 |
+
|
| 127 |
+
{self.Core.brand_voice}
|
| 128 |
"""
|
| 129 |
|
| 130 |
return context
|
|
|
|
| 139 |
|
| 140 |
instructions = f"""
|
| 141 |
|
| 142 |
+
### **Main instructions**
|
| 143 |
+
|
| 144 |
+
{self.Core.CTA} \n
|
| 145 |
"""
|
| 146 |
|
| 147 |
return instructions
|
|
|
|
| 301 |
|
| 302 |
if self.Core.platform == "push":
|
| 303 |
instructions = f"""
|
| 304 |
+
### ** General Specifications: **
|
| 305 |
|
| 306 |
+
- Start directly with the message content without greetings or closing phrases.
|
| 307 |
+
- Avoid using same or similar words so close together in "message" and "header", and make sure there is no grammar problem.
|
| 308 |
+
- The message, vocabulary and sentences **MUST** sound like a natural conversation: something that people normally say in daily conversations.
|
| 309 |
+
- {message_style}
|
| 310 |
|
| 311 |
"""
|
| 312 |
|
|
|
|
| 354 |
|
| 355 |
example_output = self.example_output()
|
| 356 |
general_instructions = f"""
|
| 357 |
+
- The "header" must be less than 30 character.
|
| 358 |
+
- The "message" must be less than 100 character.
|
| 359 |
+
- Preserve special characters and emojis in the message, you are **ONLY ALLOWED**allowed to use {self.Core.get_emoji()} emoji if needed, ONLY ONCE, and ONLY at the end of header or message).
|
| 360 |
+
- Ensure that the output is a valid JSON and not include any text outside the JSON code block.
|
| 361 |
"""
|
| 362 |
|
| 363 |
instructions = f"""
|
| 364 |
+
Expected output structure:
|
| 365 |
|
| 366 |
+
{{
|
| 367 |
+
"header": "Generated title",
|
| 368 |
+
"message": "Generated message",
|
| 369 |
+
}}
|
| 370 |
|
| 371 |
+
{general_instructions}
|
| 372 |
"""
|
| 373 |
|
| 374 |
output_instructions = f"""
|
| 375 |
+
### **Output instructions**:
|
| 376 |
|
| 377 |
+
{example_output}
|
| 378 |
+
{instructions}
|
| 379 |
"""
|
| 380 |
|
| 381 |
return output_instructions
|