fjarsra commited on
Commit
85810fc
·
verified ·
1 Parent(s): 83ab5e8

Update app/services/llm_engine.py

Browse files
Files changed (1) hide show
  1. app/services/llm_engine.py +65 -65
app/services/llm_engine.py CHANGED
@@ -52,8 +52,9 @@ class LLMEngine:
52
  print("❌ Semua Token Gagal/Habis.")
53
  raise last_error
54
 
55
- async def process_user_intent(self, user_text: str, available_skills: list):
56
  skills_str = "\n".join([f"- {s}" for s in available_skills])
 
57
 
58
  system_prompt = f"""
59
  ROLE: Kamu adalah 'Router' untuk MORA, sebuah AI Learning Assistant.
@@ -61,9 +62,10 @@ class LLMEngine:
61
 
62
  DAFTAR SKILL TERSEDIA DI DATABASE:
63
  {skills_str}
 
64
 
65
  INSTRUKSI UTAMA:
66
- Analisis pesan user dan tentukan ACTION JSON.
67
 
68
  1. ACTION: "START_EXAM"
69
  - Trigger: User ingin "tes", "ujian", "uji kemampuan", "soal", atau menyebut topik teknis (SQL, Python, CV, NLP). kalau user tidak menyebut keyword teknis dari subskill maka akan menampilkan list dari sub skill yang ada.
@@ -79,8 +81,8 @@ class LLMEngine:
79
  - PENTING: Jangan pilih ini jika user hanya ingin melihat data progress, nilai/laporan.
80
 
81
  3. ACTION: "START_PSYCH_TEST"
82
- - Trigger: Role User masih kosong, tanya "karir", "cocok kerja apa", "tes minat".
83
- - PENTING : INI UNTUK USER YNG ROLENYA MASIH KOSONG
84
 
85
  3. ACTION: "CHECK_PROGRESS" (FOKUS: PROGRESS / DATA / LAPORAN / NILAI )
86
  - Trigger: User ingin melihat HASIL belajarnya, statistiknya, progressnya, atau pencapaiannya sejauh ini.
@@ -100,22 +102,34 @@ class LLMEngine:
100
  "detected_skills": ["Nama Skill Database 1", "Nama Skill Database 2"] (Array berisi String nama skill persis dari daftar diatas. Kosongkan jika tidak ada.)
101
  }}
102
  """
 
 
 
 
 
 
 
 
 
 
 
103
 
104
  try:
105
- response_content = await self._execute_with_retry(
106
- messages=[
107
- {"role": "system", "content": system_prompt},
108
- {"role": "user", "content": user_text}
109
- ],
110
  model="llama-3.3-70b-versatile",
111
  temperature=0.0,
112
  response_format={"type": "json_object"}
113
  )
 
 
 
114
  return json.loads(response_content)
 
115
  except Exception as e:
116
- print(f"Error Router: {e}")
117
  return {"action": "CASUAL_CHAT", "detected_skills": []}
118
-
119
  async def generate_question(self, topics: list, level: str):
120
  topics_str = ", ".join(topics)
121
  prompt = f"""
@@ -176,70 +190,56 @@ class LLMEngine:
176
  print(f"ERROR Evaluate answer: {e}")
177
  return {"score": 0, "feedback": "Error menilai.", "is_correct": False}
178
 
179
- async def casual_chat(self, user_text: str, history: list = [], keyword_context: str = "", dataset_status: str = "NOT_FOUND"):
180
-
181
- if dataset_status == "FOUND":
182
- system_instruction = f"""
183
- [STATUS: VALID]
184
- User bertanya tentang topik teknis yang ADA dalam dataset skill kamu: **[{keyword_context}]**.
185
-
186
- TUGAS:
187
- 1. Jawab pertanyaan user tentang topik tersebut dengan konseptual yang singkat, padat, dan mudah dimengerti.
188
- 2. Fokus jawabanmu HANYA pada keyword tersebut.
189
- 3. Gunakan analogi sederhana jika perlu. Jangan terlalu kaku seperti buku teks, tapi tetap akurat.
190
- 4. Gaya bahasa: Ramah, Suportif, Mentor IT.
191
- 5. Giring user untuk menggunakan fitur belajar seperti tanya tentang skill teknis, Ujian/Tes sub skill, cek progres, rekomendasi belajar.
192
- """
193
- else:
194
- # SKENARIO 2: Keyword Tidak Ditemukan (TOLAK)
195
- system_instruction = f"""
196
- [STATUS: INVALID / OUT OF SCOPE]
197
- User bertanya tentang topik yang TIDAK ditemukan dalam 'Skill Keywords Dataset' kamu.
198
-
199
- TUGAS:
200
- 1. **TOLAK** untuk menjawab pertanyaan ini.
201
- 2. Katakan dengan sopan seperti: "Maaf, topik ini tidak ada dalam database skill yang saya pelajari."
202
- 3. JANGAN mencoba menjawab atau menebak, meskipun kamu tahu jawabannya secara umum. Patuhi whitelist dataset.
203
- 5. Tawarkan user untuk menggunakan fitur belajar seperti tanya tentang skill teknis, Ujian/Tes sub skill, cek progres, rekomendasi belajar.
204
- """
205
-
206
- prompt_template = f"""
207
- [ROLE]
208
- Namamu MORA. Kamu adalah Mentor & Asisten Teknis Spesialis.
209
 
210
- {system_instruction}
211
-
212
- **BATASAN (BLACK LIST):**
213
- - Jika topik TIDAK ADA di skill keyword (misal: Mobil, Kendaraan, dll), TOLAK dengan sopan dan pivot ke materi silabus.
214
- - JANGAN BERIKAN KODE FULL. Jika user minta "Buatkan kodingan", arahkan mereka untuk mengambil Ujian/Tes. Kamu hanya menjelaskan *Konsep* dan *Logika*.
215
- - Hindari jawaban yang terlalu panjang (lebih dari 4 kalimat).
216
- - Jika tidak yakin, katakan "Maaf, itu di luar pengetahuan saya."
217
- - Jangan buat-buat jawaban untuk topik di luar silabus.
218
-
219
- [PERSONALITY]
220
- Ramah, Suportif, Emoji secukupnya.
221
  """
222
-
223
- system_msg = {
224
- "role": "system",
225
- "content": prompt_template
226
- }
227
 
228
- messages = [system_msg]
 
 
 
 
 
229
  for msg in history[-5:]:
230
  messages.append({"role": msg['role'], "content": msg['content']})
 
 
231
  messages.append({"role": "user", "content": user_text})
232
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
233
  try:
234
- return await self._execute_with_retry(
235
  messages=messages,
236
- model="llama-3.3-70b-versatile",
237
- temperature=0.3
238
  )
 
239
  except Exception as e:
240
- print(f"ERROR Casual chat: {e}")
241
- return f"Maaf, otak saya sedang error. (Error: {str(e)})"
242
-
243
 
244
  async def analyze_psych_result(self, role: str, traits: list[str]):
245
  """
 
52
  print("❌ Semua Token Gagal/Habis.")
53
  raise last_error
54
 
55
+ async def process_user_intent(self, user_text: str, available_skills: list, user_role: str = "", history: list = []):
56
  skills_str = "\n".join([f"- {s}" for s in available_skills])
57
+ role_status_str = "USER BELUM MEMILIKI ROLE (ROLE KOSONG)." if not user_role else f"USER ROLE: {user_role}"
58
 
59
  system_prompt = f"""
60
  ROLE: Kamu adalah 'Router' untuk MORA, sebuah AI Learning Assistant.
 
62
 
63
  DAFTAR SKILL TERSEDIA DI DATABASE:
64
  {skills_str}
65
+ STATUS USER SAAT INI: {role_status_str}
66
 
67
  INSTRUKSI UTAMA:
68
+ Analisis pesan user DAN HISTORY PERCAKAPAN untuk menentukan ACTION JSON.
69
 
70
  1. ACTION: "START_EXAM"
71
  - Trigger: User ingin "tes", "ujian", "uji kemampuan", "soal", atau menyebut topik teknis (SQL, Python, CV, NLP). kalau user tidak menyebut keyword teknis dari subskill maka akan menampilkan list dari sub skill yang ada.
 
81
  - PENTING: Jangan pilih ini jika user hanya ingin melihat data progress, nilai/laporan.
82
 
83
  3. ACTION: "START_PSYCH_TEST"
84
+ - Trigger: Role Kosong, tanya "karir", "cocok kerja apa" user bingung minat.
85
+ - KONTEKS: Jika di history BOT menawarakan tes minat, dan User jawab "Mau/Ya/Boleh", PILIH INI.
86
 
87
  3. ACTION: "CHECK_PROGRESS" (FOKUS: PROGRESS / DATA / LAPORAN / NILAI )
88
  - Trigger: User ingin melihat HASIL belajarnya, statistiknya, progressnya, atau pencapaiannya sejauh ini.
 
102
  "detected_skills": ["Nama Skill Database 1", "Nama Skill Database 2"] (Array berisi String nama skill persis dari daftar diatas. Kosongkan jika tidak ada.)
103
  }}
104
  """
105
+ messages = [{"role": "system", "content": system_prompt}]
106
+
107
+ # Masukkan 5 chat terakhir dari history agar AI tau konteks
108
+ # Kita pastikan formatnya dictionary
109
+ for msg in history[-5:]:
110
+ role = msg.get('role') if isinstance(msg, dict) else msg.role
111
+ content = msg.get('content') if isinstance(msg, dict) else msg.content
112
+ messages.append({"role": role, "content": content})
113
+
114
+ # Masukkan pesan user saat ini (paling baru)
115
+ messages.append({"role": "user", "content": user_text})
116
 
117
  try:
118
+ chat_completion = await self.client.chat.completions.create(
119
+ messages=messages, # <--- Kirim list messages yang sudah ada history-nya
 
 
 
120
  model="llama-3.3-70b-versatile",
121
  temperature=0.0,
122
  response_format={"type": "json_object"}
123
  )
124
+
125
+ response_content = chat_completion.choices[0].message.content
126
+ print(f"DEBUG AI MAPPING: {response_content}")
127
  return json.loads(response_content)
128
+
129
  except Exception as e:
130
+ print(f"Error Intent: {e}")
131
  return {"action": "CASUAL_CHAT", "detected_skills": []}
132
+
133
  async def generate_question(self, topics: list, level: str):
134
  topics_str = ", ".join(topics)
135
  prompt = f"""
 
190
  print(f"ERROR Evaluate answer: {e}")
191
  return {"score": 0, "feedback": "Error menilai.", "is_correct": False}
192
 
193
+ async def casual_chat(self, user_text: str, history: list = [], is_role_empty: bool = False):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
 
195
+ # 1. BASE SYSTEM PROMPT (Identitas Utama)
196
+ # Ini selalu ada, baik role kosong maupun tidak.
197
+ base_system_prompt = """
198
+ Kamu adalah MORA, asisten belajar AI yang ramah, suportif, dan kekinian.
199
+ Jawablah dengan singkat, padat, dan menggunakan emoji sesekali.
 
 
 
 
 
 
200
  """
 
 
 
 
 
201
 
202
+ messages = [
203
+ {"role": "system", "content": base_system_prompt}
204
+ ]
205
+
206
+ # 2. INSERT HISTORY (Normal Flow)
207
+ # Kita tetap masukkan history agar konteks obrolan nyambung
208
  for msg in history[-5:]:
209
  messages.append({"role": msg['role'], "content": msg['content']})
210
+
211
+ # 3. INSERT USER MESSAGE (Normal Flow)
212
  messages.append({"role": "user", "content": user_text})
213
+
214
+ # 4. INJECT RESTRICTION (JIKA ROLE KOSONG)
215
+ # Kita taruh ini DI PALING BAWAH (Setelah user message).
216
+ # Tujuannya: Mengoreksi jika user minta aneh-aneh.
217
+ if is_role_empty:
218
+ restriction_msg = """
219
+ [SYSTEM ALERT - PRIORITY HIGH]
220
+ Status User: ROLE KOSONG (Belum memilih jalur karir).
221
+
222
+ INSTRUKSI RESPON:
223
+ 1. JIKA user meminta Soal/Tes/Coding/rekomendasi/progress:
224
+ TOLAK permintaan tersebut dengan halus.
225
+ Katakan: "Wah semangat banget! 🔥 Tapi pilih Role dulu yuk biar jelas arahnya. Mau coba Tes Minat?" tetapi sesuaikan lagi konteksnya
226
+
227
+ 2. JIKA user hanya menyapa/curhat:
228
+ Respon normal, tapi selipkan saran untuk memilih Role atau Tes Minat.
229
+ """
230
+ # Masukkan sebagai role 'system' agar dipatuhi
231
+ messages.append({"role": "system", "content": restriction_msg})
232
+
233
+ # 5. EXECUTE LLM
234
  try:
235
+ completion = await self.client.chat.completions.create(
236
  messages=messages,
237
+ model="llama-3.1-8b-instant",
238
+ temperature=0.6
239
  )
240
+ return completion.choices[0].message.content
241
  except Exception as e:
242
+ return f"Maaf, error sistem: {str(e)}"
 
 
243
 
244
  async def analyze_psych_result(self, role: str, traits: list[str]):
245
  """