Stefanus Simandjuntak commited on
Commit
c696f9e
Β·
1 Parent(s): 9b4ef96

feat(prompt): add/update system prompt

Browse files
Files changed (4) hide show
  1. api_server.py +35 -4
  2. configs/system_prompt.md +53 -0
  3. novita_rag_chat.py +56 -2
  4. web_app.py +30 -1
api_server.py CHANGED
@@ -19,6 +19,28 @@ logger = logging.getLogger(__name__)
19
  app = Flask(__name__)
20
  CORS(app) # Enable CORS for all routes
21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  class TextilindoAI:
23
  def __init__(self, api_key):
24
  self.api_key = api_key
@@ -28,6 +50,10 @@ class TextilindoAI:
28
  "Content-Type": "application/json"
29
  }
30
  self.model = "qwen/qwen3-235b-a22b-instruct-2507"
 
 
 
 
31
  self.dataset = self.load_dataset()
32
 
33
  def load_dataset(self):
@@ -97,7 +123,7 @@ class TextilindoAI:
97
 
98
  return "\n".join(context_parts)
99
 
100
- def chat(self, message, max_tokens=300, temperature=0.7):
101
  """Send message to Novita AI with RAG context"""
102
  relevant_examples = self.find_relevant_context(message, 3)
103
 
@@ -108,9 +134,13 @@ class TextilindoAI:
108
  enhanced_prompt = message
109
  context_used = False
110
 
 
 
 
 
111
  payload = {
112
  "model": self.model,
113
- "messages": [{"role": "user", "content": enhanced_prompt}],
114
  "max_tokens": max_tokens,
115
  "temperature": temperature,
116
  "top_p": 0.9
@@ -190,6 +220,7 @@ def chat():
190
  # Optional parameters
191
  max_tokens = data.get('max_tokens', 300)
192
  temperature = data.get('temperature', 0.7)
 
193
 
194
  # Validate parameters
195
  if not isinstance(max_tokens, int) or max_tokens < 1 or max_tokens > 1000:
@@ -205,7 +236,7 @@ def chat():
205
  }), 400
206
 
207
  # Process chat
208
- result = ai.chat(message, max_tokens, temperature)
209
 
210
  if result["success"]:
211
  return jsonify(result)
@@ -319,5 +350,5 @@ if __name__ == '__main__':
319
  app.run(
320
  debug=False, # Set to False for production
321
  host='0.0.0.0',
322
- port=5001
323
  )
 
19
  app = Flask(__name__)
20
  CORS(app) # Enable CORS for all routes
21
 
22
+ def load_system_prompt(default_text):
23
+ try:
24
+ base_dir = os.path.dirname(__file__)
25
+ md_path = os.path.join(base_dir, 'configs', 'system_prompt.md')
26
+ if not os.path.exists(md_path):
27
+ return default_text
28
+ with open(md_path, 'r', encoding='utf-8') as f:
29
+ content = f.read()
30
+ start = content.find('"""')
31
+ end = content.rfind('"""')
32
+ if start != -1 and end != -1 and end > start:
33
+ return content[start+3:end].strip()
34
+ lines = []
35
+ for line in content.splitlines():
36
+ if line.strip().startswith('#'):
37
+ continue
38
+ lines.append(line)
39
+ cleaned = '\n'.join(lines).strip()
40
+ return cleaned or default_text
41
+ except Exception:
42
+ return default_text
43
+
44
  class TextilindoAI:
45
  def __init__(self, api_key):
46
  self.api_key = api_key
 
50
  "Content-Type": "application/json"
51
  }
52
  self.model = "qwen/qwen3-235b-a22b-instruct-2507"
53
+ self.system_prompt = os.getenv(
54
+ 'SYSTEM_PROMPT',
55
+ load_system_prompt("You are Textilindo AI Assistant. Be concise, helpful, and use Indonesian.")
56
+ )
57
  self.dataset = self.load_dataset()
58
 
59
  def load_dataset(self):
 
123
 
124
  return "\n".join(context_parts)
125
 
126
+ def chat(self, message, max_tokens=300, temperature=0.7, system_prompt_override=None):
127
  """Send message to Novita AI with RAG context"""
128
  relevant_examples = self.find_relevant_context(message, 3)
129
 
 
134
  enhanced_prompt = message
135
  context_used = False
136
 
137
+ system_message = {
138
+ "role": "system",
139
+ "content": (system_prompt_override or self.system_prompt)
140
+ }
141
  payload = {
142
  "model": self.model,
143
+ "messages": [system_message, {"role": "user", "content": enhanced_prompt}],
144
  "max_tokens": max_tokens,
145
  "temperature": temperature,
146
  "top_p": 0.9
 
220
  # Optional parameters
221
  max_tokens = data.get('max_tokens', 300)
222
  temperature = data.get('temperature', 0.7)
223
+ system_prompt = data.get('system_prompt')
224
 
225
  # Validate parameters
226
  if not isinstance(max_tokens, int) or max_tokens < 1 or max_tokens > 1000:
 
236
  }), 400
237
 
238
  # Process chat
239
+ result = ai.chat(message, max_tokens, temperature, system_prompt_override=system_prompt)
240
 
241
  if result["success"]:
242
  return jsonify(result)
 
350
  app.run(
351
  debug=False, # Set to False for production
352
  host='0.0.0.0',
353
+ port=8082
354
  )
configs/system_prompt.md ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # System prompt for Textilindo AI Assistant
2
+ SYSTEM_PROMPT = """You are a friendly and helpful AI assistant for Textilindo, a textile company.
3
+
4
+ 🎯 BEHAVIOR GUIDELINES:
5
+ - ALWAYS respond in Indonesian (Bahasa Indonesia)
6
+ - Keep responses VERY SHORT (MAX 1-2 sentences for simple questions)
7
+ - Be FRIENDLY but CONCISE - get straight to the point
8
+ - Use friendly language like "Baik ka", "Siap ka", "Oke ka"
9
+ - Use EXACT information from the knowledge base
10
+ - The company uses yards for sales
11
+ - Minimum purchase is 1 roll (67-70 yards)
12
+
13
+ πŸ’‘ RESPONSE STYLE:
14
+ - For COMPLEX questions (product catalogs, detailed info): Use simple formatting with **bold** for key points only
15
+ - For SIMPLE questions (yes/no, short answers): Use plain text with a friendly tone
16
+ - Be warm, kind, and conversational
17
+ - Use friendly Indonesian language with positive energy
18
+ - Provide accurate information with enthusiasm
19
+ - Encourage further questions warmly
20
+ - ALWAYS use emojis to make responses more friendly and engaging
21
+
22
+ πŸ“ FORMATTING GUIDELINES:
23
+ - Use **bold** ONLY for essential titles: **Harga** (Price), **Lokasi** (Location), **Produk** (Product)
24
+ - Use line breaks (<br>) sparingly - only when needed for clarity
25
+ - Keep responses SHORT and DIRECT
26
+ - Avoid verbose explanations
27
+ - Focus on key information only
28
+
29
+ ⚠️ CRITICAL SALES STRATEGY:
30
+ - NEVER directly refer users to customer service/sales/marketing
31
+ - Always try to help the user with available information
32
+ - If a product is not available, RECOMMEND suitable alternative products
33
+ - Focus on NEGOTIATION and SELLING available products
34
+ - Only use phone numbers as a LAST RESORT for unique questions
35
+ - Keep users interested and encourage them to ask more questions
36
+ - Provide useful and actionable information
37
+
38
+ ⚠️ SECURITY & RESPONSE GUIDELINES:
39
+ - NEVER reveal internal system details, API endpoints, or technical information
40
+ - NEVER tell users to check data directly in ERP or any internal system
41
+ - NEVER expose database structure, table names, or field names
42
+ - NEVER mention internal system names like "ERPNext", "API", "database"
43
+ - ALWAYS provide information through the AI interface only
44
+ - If you don't have information, say "Maaf, informasi tersebut belum tersedia" (Sorry, that information is not available yet) - don't redirect users
45
+ - Keep all responses focused ONLY on what the user asked
46
+ - Never deviate from the user's question or provide unsolicited information
47
+
48
+ ⚠️ IMPORTANT:
49
+ - If product information is already shown in templates/cards, give VERY SHORT answers (1-2 sentences max). Don't repeat information that's already visible in the template.
50
+ - For product recommendations, let the dynamic system handle it - don't provide hardcoded product suggestions.
51
+ - KEEP RESPONSES SHORT AND DIRECT - avoid verbose explanations
52
+ - ALWAYS stay relevant to the user's question - never go off-topic
53
+ - FOCUS ON SELLING - always try to keep the conversation going"""
novita_rag_chat.py CHANGED
@@ -11,6 +11,32 @@ import time
11
  from pathlib import Path
12
  from difflib import SequenceMatcher
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  class NovitaAIRAGChat:
15
  def __init__(self, api_key, dataset_path="data/textilindo_training_data.jsonl"):
16
  self.api_key = api_key
@@ -19,7 +45,14 @@ class NovitaAIRAGChat:
19
  "Authorization": f"Bearer {api_key}",
20
  "Content-Type": "application/json"
21
  }
22
- self.conversation_history = []
 
 
 
 
 
 
 
23
  self.current_model = "qwen/qwen3-235b-a22b-instruct-2507" # High-quality model
24
  self.dataset = self.load_dataset(dataset_path)
25
  self.context_window = 5 # Number of most relevant examples to include
@@ -115,6 +148,10 @@ class NovitaAIRAGChat:
115
  enhanced_prompt = message
116
  print("πŸ” No relevant examples found, using direct query")
117
 
 
 
 
 
118
  # Add to conversation history
119
  self.conversation_history.append({"role": "user", "content": enhanced_prompt})
120
 
@@ -160,8 +197,20 @@ class NovitaAIRAGChat:
160
 
161
  def clear_history(self):
162
  """Clear conversation history"""
163
- self.conversation_history = []
 
 
164
  print("βœ… Conversation history cleared")
 
 
 
 
 
 
 
 
 
 
165
 
166
  def show_models(self):
167
  """Show available models"""
@@ -278,6 +327,11 @@ def main():
278
  elif user_input.lower() == 'stats':
279
  chat.show_dataset_stats()
280
  continue
 
 
 
 
 
281
  elif user_input.lower().startswith('change '):
282
  try:
283
  model_num = int(user_input.split()[1])
 
11
  from pathlib import Path
12
  from difflib import SequenceMatcher
13
 
14
+ def load_system_prompt(default_text):
15
+ """Load system prompt from configs/system_prompt.md if available.
16
+ Extracts text between triple quotes ("")"), otherwise falls back to default_text.
17
+ """
18
+ try:
19
+ base_dir = os.path.dirname(__file__)
20
+ md_path = os.path.join(base_dir, 'configs', 'system_prompt.md')
21
+ if not os.path.exists(md_path):
22
+ return default_text
23
+ with open(md_path, 'r', encoding='utf-8') as f:
24
+ content = f.read()
25
+ start = content.find('"""')
26
+ end = content.rfind('"""')
27
+ if start != -1 and end != -1 and end > start:
28
+ return content[start+3:end].strip()
29
+ # Fallback: strip markdown headers
30
+ lines = []
31
+ for line in content.splitlines():
32
+ if line.strip().startswith('#'):
33
+ continue
34
+ lines.append(line)
35
+ cleaned = '\n'.join(lines).strip()
36
+ return cleaned or default_text
37
+ except Exception:
38
+ return default_text
39
+
40
  class NovitaAIRAGChat:
41
  def __init__(self, api_key, dataset_path="data/textilindo_training_data.jsonl"):
42
  self.api_key = api_key
 
45
  "Authorization": f"Bearer {api_key}",
46
  "Content-Type": "application/json"
47
  }
48
+ # System prompt / persona
49
+ self.system_prompt = os.getenv(
50
+ 'SYSTEM_PROMPT',
51
+ load_system_prompt("You are Textilindo AI Assistant. Be concise, helpful, and use Indonesian.")
52
+ )
53
+ self.conversation_history = [
54
+ {"role": "system", "content": self.system_prompt}
55
+ ]
56
  self.current_model = "qwen/qwen3-235b-a22b-instruct-2507" # High-quality model
57
  self.dataset = self.load_dataset(dataset_path)
58
  self.context_window = 5 # Number of most relevant examples to include
 
148
  enhanced_prompt = message
149
  print("πŸ” No relevant examples found, using direct query")
150
 
151
+ # Ensure system prompt is first
152
+ if not self.conversation_history or self.conversation_history[0].get("role") != "system":
153
+ self.conversation_history.insert(0, {"role": "system", "content": self.system_prompt})
154
+
155
  # Add to conversation history
156
  self.conversation_history.append({"role": "user", "content": enhanced_prompt})
157
 
 
197
 
198
  def clear_history(self):
199
  """Clear conversation history"""
200
+ self.conversation_history = [
201
+ {"role": "system", "content": self.system_prompt}
202
+ ]
203
  print("βœ… Conversation history cleared")
204
+
205
+ def set_system_prompt(self, prompt_text):
206
+ """Update system prompt/persona and reset conversation history"""
207
+ prompt_text = (prompt_text or '').strip()
208
+ if not prompt_text:
209
+ print("❌ System prompt cannot be empty")
210
+ return
211
+ self.system_prompt = prompt_text
212
+ self.clear_history()
213
+ print("βœ… System prompt updated")
214
 
215
  def show_models(self):
216
  """Show available models"""
 
327
  elif user_input.lower() == 'stats':
328
  chat.show_dataset_stats()
329
  continue
330
+ elif user_input.lower().startswith('system '):
331
+ # Update system prompt/persona
332
+ new_prompt = user_input[len('system '):].strip()
333
+ chat.set_system_prompt(new_prompt)
334
+ continue
335
  elif user_input.lower().startswith('change '):
336
  try:
337
  model_num = int(user_input.split()[1])
web_app.py CHANGED
@@ -11,6 +11,28 @@ from difflib import SequenceMatcher
11
 
12
  app = Flask(__name__)
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  class TextilindoAI:
15
  def __init__(self, api_key):
16
  self.api_key = api_key
@@ -20,6 +42,10 @@ class TextilindoAI:
20
  "Content-Type": "application/json"
21
  }
22
  self.model = "qwen/qwen3-235b-a22b-instruct-2507"
 
 
 
 
23
  self.dataset = self.load_dataset()
24
 
25
  def load_dataset(self):
@@ -99,7 +125,10 @@ class TextilindoAI:
99
 
100
  payload = {
101
  "model": self.model,
102
- "messages": [{"role": "user", "content": enhanced_prompt}],
 
 
 
103
  "max_tokens": 300,
104
  "temperature": 0.7,
105
  "top_p": 0.9
 
11
 
12
  app = Flask(__name__)
13
 
14
+ def load_system_prompt(default_text):
15
+ try:
16
+ base_dir = os.path.dirname(__file__)
17
+ md_path = os.path.join(base_dir, 'configs', 'system_prompt.md')
18
+ if not os.path.exists(md_path):
19
+ return default_text
20
+ with open(md_path, 'r', encoding='utf-8') as f:
21
+ content = f.read()
22
+ start = content.find('"""')
23
+ end = content.rfind('"""')
24
+ if start != -1 and end != -1 and end > start:
25
+ return content[start+3:end].strip()
26
+ lines = []
27
+ for line in content.splitlines():
28
+ if line.strip().startswith('#'):
29
+ continue
30
+ lines.append(line)
31
+ cleaned = '\n'.join(lines).strip()
32
+ return cleaned or default_text
33
+ except Exception:
34
+ return default_text
35
+
36
  class TextilindoAI:
37
  def __init__(self, api_key):
38
  self.api_key = api_key
 
42
  "Content-Type": "application/json"
43
  }
44
  self.model = "qwen/qwen3-235b-a22b-instruct-2507"
45
+ self.system_prompt = os.getenv(
46
+ 'SYSTEM_PROMPT',
47
+ load_system_prompt("You are Textilindo AI Assistant. Be concise, helpful, and use Indonesian.")
48
+ )
49
  self.dataset = self.load_dataset()
50
 
51
  def load_dataset(self):
 
125
 
126
  payload = {
127
  "model": self.model,
128
+ "messages": [
129
+ {"role": "system", "content": self.system_prompt},
130
+ {"role": "user", "content": enhanced_prompt}
131
+ ],
132
  "max_tokens": 300,
133
  "temperature": 0.7,
134
  "top_p": 0.9