klydekushy commited on
Commit
f94f7e6
·
verified ·
1 Parent(s): 7dff30a

Update src/modules/jasmine_agent.py

Browse files
Files changed (1) hide show
  1. src/modules/jasmine_agent.py +57 -64
src/modules/jasmine_agent.py CHANGED
@@ -1,7 +1,7 @@
1
  """
2
- MODULE JASMINE AGENT - V50 (AIP LOGIC KERNEL ADAPTED)
3
- =====================================================
4
- Mise à jour : Intègre le raisonnement explicite (Chain of Thought) dans l'architecture existante.
5
  """
6
  import google.generativeai as genai
7
  from groq import Groq
@@ -46,44 +46,43 @@ PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
46
  PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
47
  """
48
 
49
- def build_base_system_prompt(self):
50
  return f"""
51
- ROLE: CERVEAU NUMÉRIQUE (AIP LOGIC KERNEL).
52
- MISSION: Résoudre les requêtes opérationnelles en ancrant chaque décision dans la vérité de l'Ontologie.
53
-
54
- --- RÈGLES DE NAVIGATION (ENTITÉS vs NOMS) ---
55
- TRÈS IMPORTANT : Les prédicats comme 'a_emprunteur' ou 'est_garanti_par' lient des IDs techniques (ex: vortex:CLI-2026-0001).
56
- Pour chercher par NOM (ex: 'Aichatou'), tu dois impérativement faire une jointure :
57
- 1. Lier l'ID : ?pret vortex:a_emprunteur ?client .
58
- 2. Lier le Nom : ?client vortex:nom ?nom . (ou rdfs:label)
59
- 3. Filtrer : FILTER(CONTAINS(LCASE(STR(?nom)), 'aichatou'))
60
-
61
- --- RÈGLES DE CALCUL & FILTRES (OAG STRICT) ---
62
- Pour toute question impliquant des chiffres (sommes, moyennes, seuils > 100k) :
63
- 1. UTILISE l'outil 'execute_sparql'.
64
- 2. NE JAMAIS utiliser 'search_semantic' pour un filtre numérique.
65
- 3. PRIORITÉ GOLDEN RECORD : Utilise 'vortex:montant_net' pour les calculs de somme afin d'éviter les doublons Master/Update.
66
-
67
- --- RÈGLES CRITIQUES (SPARQL) ---
68
- 1. Namespace UNIQUE : <http://vortex.ai/ontology#> (prefix: vortex).
69
- 2. Préfixes OBLIGATOIRES :
 
 
 
 
 
 
 
 
70
  {self._get_prefixes()}
71
 
72
- --- RÈGLES DE RECHERCHE (TEXTE) ---
73
- Si l'utilisateur cherche un NOM ou un TEXTE :
74
- NE JAMAIS utiliser l'égalité stricte (=). Utilise TOUJOURS :
75
- FILTER(CONTAINS(LCASE(STR(?variable)), 'koulnan'))
76
-
77
- --- PROCESSUS DE PENSÉE (CHAIN OF THOUGHT) ---
78
- Structure ton raisonnement dans 'thought_trace' :
79
- 1. ANALYSE : Intention (Calcul, Navigation 360°, Recherche).
80
- 2. EXTRACTION : Liste les entités (ex: Client:Aichatou).
81
- 3. NAVIGATION : Identifie les sauts de jointure nécessaires (Prêt -> Client -> Nom).
82
- 4. OUTIL : Choix de l'outil déterministe.
83
-
84
- CONTRAINTES :
85
- 1. Réponds UNIQUEMENT en JSON.
86
- 2. Si une donnée manque, renvoie "tool": "none".
87
  """
88
 
89
  def _format_messages_for_groq(self, system_prompt, chat_history, user_message):
@@ -124,13 +123,6 @@ CONTRAINTES :
124
  --- 🎯 PLAN TACTIQUE ---
125
  STRATÉGIE: {plan['strategy']}
126
  CONTEXTE: {plan['reason']}
127
-
128
- ⚠️ SORTIE JSON OBLIGATOIRE :
129
- {{
130
- "thought_trace": "1. ANALYSE: ... 2. EXTRACTION: ... 3. NAVIGATION: ...",
131
- "tool": "{plan['tool']}",
132
- "args": {{ "query": "..." }}
133
- }}
134
  """
135
  last_error = None
136
  for model_name in self.MODEL_CASCADE:
@@ -140,35 +132,36 @@ CONTEXTE: {plan['reason']}
140
  if not self.google_key: continue
141
  msgs = self._format_messages_for_gemini(full_system_prompt, chat_history, user_message)
142
  model = genai.GenerativeModel(model_name)
143
- res = model.generate_content(msgs)
 
144
  response_text = res.text
145
  else:
146
  if not self.groq_client: continue
147
  msgs = self._format_messages_for_groq(full_system_prompt, chat_history, user_message)
148
  completion = self.groq_client.chat.completions.create(
149
- model=model_name, messages=msgs, temperature=0.0
150
  )
151
  response_text = completion.choices[0].message.content
152
 
153
  clean_text = response_text.strip()
154
- match = re.search(r'(\{.*"tool":.*\})', clean_text, re.DOTALL)
 
155
 
156
- action = None
157
- thought_trace = "Raisonnement non disponible."
158
-
159
- if match:
160
- try:
161
- json_str = match.group(1)
162
- action = json.loads(json_str)
163
- thought_trace = action.get("thought_trace", planning_log)
164
-
165
- if action.get("tool") == "execute_sparql":
166
- raw_query = action["args"].get("query", "")
167
- if "PREFIX vortex:" not in raw_query:
168
- action["args"]["query"] = self._get_prefixes() + "\n" + raw_query
169
-
170
- return "", action, thought_trace
171
- except: pass
172
 
173
  return clean_text, None, planning_log
174
 
 
1
  """
2
+ MODULE JASMINE AGENT - V FINAL (AIP LOGIC KERNEL + GROQ)
3
+ ========================================================
4
+ Responsabilité : Traduire le langage naturel en actions ontologiques précises.
5
  """
6
  import google.generativeai as genai
7
  from groq import Groq
 
46
  PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
47
  """
48
 
49
+ def build_base_system_prompt(self):
50
  return f"""
51
+ ROLE: Tu es le Cerveau Numérique (AIP Logic) de VORTEX.
52
+ MISSION: Répondre aux questions en interrogeant le Knowledge Graph via SPARQL.
53
+
54
+ --- TON TERRITOIRE (ONTOLOGIE) ---
55
+ 1. Prêts : vortex:Pret (liés via 'a_emprunteur' à Client, 'est_garanti_par' à Garant).
56
+ 2. Clients : vortex:Client (propriété 'nom' ou 'rdfs:label' pour le nom complet).
57
+ 3. Montants :
58
+ - 'vortex:montant' : Donnée brute (peut contenir des doublons).
59
+ - 'vortex:montant_net' : GOLDEN RECORD (Valeur unique et fiable pour les calculs).
60
+
61
+ --- RÈGLES DE NAVIGATION (CRITIQUES) ---
62
+ Pour chercher un dossier par NOM (ex: "Koulnan" ou "Aichatou") :
63
+ NE JAMAIS faire : ?pret vortex:a_emprunteur 'Koulnan'. (Car a_emprunteur pointe vers un ID technique).
64
+ TU DOIS FAIRE UNE JOINTURE :
65
+ ?pret vortex:a_emprunteur ?client_id .
66
+ ?client_id rdfs:label ?nom_client .
67
+ FILTER(CONTAINS(LCASE(STR(?nom_client)), 'koulnan'))
68
+
69
+ --- RÈGLES DE CALCUL ---
70
+ Pour une SOMME, un MONTANT TOTAL ou un SEUIL (> 100000) :
71
+ 1. Utilise TOUJOURS 'vortex:montant_net' (le Golden Record).
72
+ 2. Utilise TOUJOURS l'outil 'execute_sparql'.
73
+ 3. NE JAMAIS utiliser 'search_semantic' pour des filtres numériques.
74
+
75
+ --- RÈGLES TECHNIQUES ---
76
+ - Namespace UNIQUE : <http://vortex.ai/ontology#> (prefix: vortex).
77
+ - Préfixes OBLIGATOIRES :
78
  {self._get_prefixes()}
79
 
80
+ CONTRAINTES DE SORTIE (JSON STRICT) :
81
+ {{
82
+ "thought_trace": "1. ANALYSE: ... 2. EXTRACTION: ... 3. NAVIGATION: ... 4. OUTIL: ...",
83
+ "tool": "execute_sparql" | "search_semantic" | "none",
84
+ "args": {{ "query": "..." }}
85
+ }}
 
 
 
 
 
 
 
 
 
86
  """
87
 
88
  def _format_messages_for_groq(self, system_prompt, chat_history, user_message):
 
123
  --- 🎯 PLAN TACTIQUE ---
124
  STRATÉGIE: {plan['strategy']}
125
  CONTEXTE: {plan['reason']}
 
 
 
 
 
 
 
126
  """
127
  last_error = None
128
  for model_name in self.MODEL_CASCADE:
 
132
  if not self.google_key: continue
133
  msgs = self._format_messages_for_gemini(full_system_prompt, chat_history, user_message)
134
  model = genai.GenerativeModel(model_name)
135
+ # Force JSON mode for Gemini
136
+ res = model.generate_content(msgs, generation_config={"response_mime_type": "application/json"})
137
  response_text = res.text
138
  else:
139
  if not self.groq_client: continue
140
  msgs = self._format_messages_for_groq(full_system_prompt, chat_history, user_message)
141
  completion = self.groq_client.chat.completions.create(
142
+ model=model_name, messages=msgs, temperature=0.0, response_format={"type": "json_object"}
143
  )
144
  response_text = completion.choices[0].message.content
145
 
146
  clean_text = response_text.strip()
147
+ # Nettoyage au cas où le modèle ajoute du markdown
148
+ clean_text = re.sub(r'```json\s*|\s*```', '', clean_text)
149
 
150
+ try:
151
+ action = json.loads(clean_text)
152
+
153
+ # Sécurité Préfixes SPARQL
154
+ if action.get("tool") == "execute_sparql":
155
+ raw_query = action["args"].get("query", "")
156
+ if "PREFIX vortex:" not in raw_query:
157
+ action["args"]["query"] = self._get_prefixes() + "\n" + raw_query
158
+
159
+ # Récupération du Chain of Thought
160
+ thought_trace = action.get("thought_trace", planning_log)
161
+
162
+ return "", action, thought_trace
163
+ except json.JSONDecodeError:
164
+ pass
 
165
 
166
  return clean_text, None, planning_log
167