AshenClock commited on
Commit
30e0c2a
·
verified ·
1 Parent(s): 99a1a6f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +42 -26
app.py CHANGED
@@ -32,7 +32,7 @@ if not API_KEY:
32
  # Definisci i percorsi dei file
33
  BASE_DIR = os.path.dirname(os.path.abspath(__file__))
34
  RDF_FILE = os.path.join(BASE_DIR, "Ontologia.rdf")
35
- HF_MODEL = "NousResearch/Hermes-3-Llama-3.1-8B" # Assicurati che il modello sia supportato
36
 
37
  MAX_CLASSES = 30
38
  MAX_PROPERTIES = 30
@@ -133,7 +133,7 @@ def create_faiss_index(documents_file: str, index_file: str, embedding_model: st
133
  with open(documents_file, "r", encoding="utf-8") as f:
134
  document = json.load(f)
135
  logger.info(f"Documento caricato da {documents_file}.")
136
-
137
  # Genera embedding
138
  model = SentenceTransformer(embedding_model)
139
  # Concatenazione delle classi, proprietà e entità per l'embedding
@@ -142,13 +142,13 @@ def create_faiss_index(documents_file: str, index_file: str, embedding_model: st
142
  texts += [f"Entità: {entity['label']}. Descrizione: {entity['description']}. Proprietà: {entity['properties']}" for entity in document.get('entities', [])]
143
  embeddings = model.encode(texts, convert_to_numpy=True)
144
  logger.info("Embedding generati con SentenceTransformer.")
145
-
146
  # Crea l'indice FAISS
147
  dimension = embeddings.shape[1]
148
  index = faiss.IndexFlatL2(dimension)
149
  index.add(embeddings)
150
  logger.info(f"Indice FAISS creato con dimensione: {dimension}.")
151
-
152
  # Salva l'indice
153
  faiss.write_index(index, index_file)
154
  logger.info(f"Indice FAISS salvato in {index_file}.")
@@ -236,11 +236,11 @@ def extract_classes_and_properties(rdf_file: str) -> str:
236
  txt_entities = "\n".join([f"- ENTITÀ: {e}" for e in entities_list])
237
 
238
  summary = f"""\
239
- # CLASSI (max {MAX_CLASSES})
240
  {txt_classes}
241
- # PROPRIETÀ (max {MAX_PROPERTIES})
242
  {txt_props}
243
- # ENTITÀ (max {MAX_CLASSES})
244
  {txt_entities}
245
  """
246
  logger.info("Estrazione di classi, proprietà ed entità completata.")
@@ -254,25 +254,25 @@ def retrieve_relevant_documents(query: str, top_k: int = 5):
254
  with open(DOCUMENTS_FILE, "r", encoding="utf-8") as f:
255
  document = json.load(f)
256
  logger.info(f"Documento caricato da {DOCUMENTS_FILE}.")
257
-
258
  # Carica l'indice FAISS
259
  index = faiss.read_index(FAISS_INDEX_FILE)
260
  logger.info(f"Indice FAISS caricato da {FAISS_INDEX_FILE}.")
261
-
262
  # Genera embedding della query
263
  model = SentenceTransformer('all-MiniLM-L6-v2')
264
  query_embedding = model.encode([query], convert_to_numpy=True)
265
  logger.info("Embedding della query generati.")
266
-
267
  # Ricerca nell'indice
268
  distances, indices = index.search(query_embedding, top_k)
269
  logger.info(f"Ricerca FAISS completata. Risultati ottenuti: {len(indices[0])}")
270
-
271
  # Concatenazione delle descrizioni per la ricerca
272
  texts = [f"Classe: {cls['label']}. Descrizione: {cls['description']}" for cls in document['classes']]
273
  texts += [f"Proprietà: {prop['label']}. Descrizione: {prop['description']}" for prop in document['properties']]
274
  texts += [f"Entità: {entity['label']}. Descrizione: {entity['description']}. Proprietà: {entity['properties']}" for entity in document.get('entities', [])]
275
-
276
  # Recupera i testi rilevanti
277
  relevant_texts = [texts[idx] for idx in indices[0] if idx < len(texts)]
278
  retrieved_docs = "\n".join(relevant_texts)
@@ -288,15 +288,17 @@ def create_system_message(ont_text: str, retrieved_docs: str) -> str:
288
  informazioni recuperate tramite RAG.
289
  """
290
  return f"""
291
- Sei un assistente museale esperto in ontologie RDF. Utilizza le informazioni fornite per generare query SPARQL precise e pertinenti. Ecco un estratto di CLASSI, PROPRIETÀ ed ENTità dell'ontologia (senza NamedIndividuals):
292
- --- ONTOLOGIA ---
 
 
293
  {ont_text}
294
- --- FINE ---
 
295
  Ecco alcune informazioni rilevanti recuperate dalla base di conoscenza:
296
  {retrieved_docs}
297
- Suggerimento: se l'utente chiede il 'materiale' di un'opera, potresti usare qualcosa come 'base:materialeOpera' o un'altra proprietà simile (se esiste). Non è tassativo: usa la proprietà che ritieni più affine se ci sono riferimenti in ontologia.
298
 
299
- REGOLE STRINGENTI:
300
  1) Se l'utente chiede informazioni su questa ontologia, genera SEMPRE una query SPARQL in UNA SOLA RIGA, con prefix:
301
  PREFIX base: <http://www.semanticweb.org/lucreziamosca/ontologies/progettoMuseo#>
302
  2) La query SPARQL deve essere precisa e cercare esattamente le entità specificate dall'utente. Ad esempio, se l'utente chiede "Chi ha creato l'opera 'Amore e Psiche'?", la query dovrebbe cercare l'opera esattamente con quel nome.
@@ -304,13 +306,16 @@ REGOLE STRINGENTI:
304
  4) Se la domanda è generica (tipo 'Ciao, come stai?'), rispondi breve.
305
  5) Se trovi risultati, la risposta finale deve essere la query SPARQL (una sola riga).
306
  6) Se non trovi nulla, rispondi con 'Nessuna info.'
307
- 7) Non multiline. Esempio: PREFIX base: <...> SELECT ?x WHERE {{ ... }}.
308
 
309
- Esempio:
310
  Utente: Chi ha creato l'opera 'Amore e Psiche'?
311
- Risposta: PREFIX base: <http://www.semanticweb.org/lucreziamosca/ontologies/progettoMuseo#> SELECT ?creatore WHERE {{ ?opera base:hasName "Amore e Psiche" . ?opera base:creatoDa ?creatore . }}
 
 
 
312
 
313
- FINE REGOLE
314
  """
315
 
316
  def create_explanation_prompt(results_str: str) -> str:
@@ -321,12 +326,12 @@ Ho ottenuto questi risultati SPARQL:
321
  Ora fornisci una breve spiegazione museale (massimo ~10 righe), senza inventare oltre i risultati.
322
  """
323
 
324
- async def call_hf_model(prompt: str, temperature: float = 0.5, max_tokens: int = 1024) -> str:
325
  """Chiama il modello Hugging Face tramite l'API REST e gestisce la risposta."""
326
  logger.debug("Chiamo HF con il seguente prompt:")
327
  content_preview = (prompt[:300] + '...') if len(prompt) > 300 else prompt
328
  logger.debug(f"PROMPT => {content_preview}")
329
-
330
  headers = {
331
  "Authorization": f"Bearer {API_KEY}"
332
  }
@@ -338,7 +343,7 @@ async def call_hf_model(prompt: str, temperature: float = 0.5, max_tokens: int =
338
  "top_p": 0.9
339
  }
340
  }
341
-
342
  try:
343
  response = requests.post(
344
  f"https://api-inference.huggingface.co/models/{HF_MODEL}",
@@ -356,7 +361,7 @@ async def call_hf_model(prompt: str, temperature: float = 0.5, max_tokens: int =
356
  raw = data["generated_text"]
357
  else:
358
  raise ValueError("Nessun campo 'generated_text' nella risposta.")
359
-
360
  # Forza la risposta su una singola linea se multilinea
361
  single_line = " ".join(raw.splitlines())
362
  logger.debug(f"Risposta HF single-line: {single_line}")
@@ -365,6 +370,11 @@ async def call_hf_model(prompt: str, temperature: float = 0.5, max_tokens: int =
365
  logger.error(f"Errore nella chiamata all'API Hugging Face tramite requests: {e}")
366
  raise HTTPException(status_code=500, detail=str(e))
367
 
 
 
 
 
 
368
  # Prepara i file necessari per RAG
369
  prepare_retrieval()
370
 
@@ -375,7 +385,7 @@ app = FastAPI()
375
 
376
  class QueryRequest(BaseModel):
377
  message: str
378
- max_tokens: int = 1024
379
  temperature: float = 0.5
380
 
381
  @app.post("/generate-response/")
@@ -383,6 +393,12 @@ async def generate_response(req: QueryRequest):
383
  user_input = req.message
384
  logger.info(f"Utente dice: {user_input}")
385
 
 
 
 
 
 
 
386
  try:
387
  # Recupera documenti rilevanti usando RAG
388
  retrieved_docs = retrieve_relevant_documents(user_input, top_k=3)
 
32
  # Definisci i percorsi dei file
33
  BASE_DIR = os.path.dirname(os.path.abspath(__file__))
34
  RDF_FILE = os.path.join(BASE_DIR, "Ontologia.rdf")
35
+ HF_MODEL = "mistralai/Mixtral-8x7B-Instruct-v0.1" # Modello ottimizzato per seguire istruzioni
36
 
37
  MAX_CLASSES = 30
38
  MAX_PROPERTIES = 30
 
133
  with open(documents_file, "r", encoding="utf-8") as f:
134
  document = json.load(f)
135
  logger.info(f"Documento caricato da {documents_file}.")
136
+
137
  # Genera embedding
138
  model = SentenceTransformer(embedding_model)
139
  # Concatenazione delle classi, proprietà e entità per l'embedding
 
142
  texts += [f"Entità: {entity['label']}. Descrizione: {entity['description']}. Proprietà: {entity['properties']}" for entity in document.get('entities', [])]
143
  embeddings = model.encode(texts, convert_to_numpy=True)
144
  logger.info("Embedding generati con SentenceTransformer.")
145
+
146
  # Crea l'indice FAISS
147
  dimension = embeddings.shape[1]
148
  index = faiss.IndexFlatL2(dimension)
149
  index.add(embeddings)
150
  logger.info(f"Indice FAISS creato con dimensione: {dimension}.")
151
+
152
  # Salva l'indice
153
  faiss.write_index(index, index_file)
154
  logger.info(f"Indice FAISS salvato in {index_file}.")
 
236
  txt_entities = "\n".join([f"- ENTITÀ: {e}" for e in entities_list])
237
 
238
  summary = f"""\
239
+ ### CLASSI (max {MAX_CLASSES})
240
  {txt_classes}
241
+ ### PROPRIETÀ (max {MAX_PROPERTIES})
242
  {txt_props}
243
+ ### ENTITÀ (max {MAX_CLASSES})
244
  {txt_entities}
245
  """
246
  logger.info("Estrazione di classi, proprietà ed entità completata.")
 
254
  with open(DOCUMENTS_FILE, "r", encoding="utf-8") as f:
255
  document = json.load(f)
256
  logger.info(f"Documento caricato da {DOCUMENTS_FILE}.")
257
+
258
  # Carica l'indice FAISS
259
  index = faiss.read_index(FAISS_INDEX_FILE)
260
  logger.info(f"Indice FAISS caricato da {FAISS_INDEX_FILE}.")
261
+
262
  # Genera embedding della query
263
  model = SentenceTransformer('all-MiniLM-L6-v2')
264
  query_embedding = model.encode([query], convert_to_numpy=True)
265
  logger.info("Embedding della query generati.")
266
+
267
  # Ricerca nell'indice
268
  distances, indices = index.search(query_embedding, top_k)
269
  logger.info(f"Ricerca FAISS completata. Risultati ottenuti: {len(indices[0])}")
270
+
271
  # Concatenazione delle descrizioni per la ricerca
272
  texts = [f"Classe: {cls['label']}. Descrizione: {cls['description']}" for cls in document['classes']]
273
  texts += [f"Proprietà: {prop['label']}. Descrizione: {prop['description']}" for prop in document['properties']]
274
  texts += [f"Entità: {entity['label']}. Descrizione: {entity['description']}. Proprietà: {entity['properties']}" for entity in document.get('entities', [])]
275
+
276
  # Recupera i testi rilevanti
277
  relevant_texts = [texts[idx] for idx in indices[0] if idx < len(texts)]
278
  retrieved_docs = "\n".join(relevant_texts)
 
288
  informazioni recuperate tramite RAG.
289
  """
290
  return f"""
291
+ ### Istruzioni ###
292
+ Sei un assistente museale esperto in ontologie RDF. Utilizza le informazioni fornite per generare query SPARQL precise e pertinenti.
293
+
294
+ ### Ontologia ###
295
  {ont_text}
296
+ ### FINE Ontologia ###
297
+
298
  Ecco alcune informazioni rilevanti recuperate dalla base di conoscenza:
299
  {retrieved_docs}
 
300
 
301
+ ### Regole Stringenti ###
302
  1) Se l'utente chiede informazioni su questa ontologia, genera SEMPRE una query SPARQL in UNA SOLA RIGA, con prefix:
303
  PREFIX base: <http://www.semanticweb.org/lucreziamosca/ontologies/progettoMuseo#>
304
  2) La query SPARQL deve essere precisa e cercare esattamente le entità specificate dall'utente. Ad esempio, se l'utente chiede "Chi ha creato l'opera 'Amore e Psiche'?", la query dovrebbe cercare l'opera esattamente con quel nome.
 
306
  4) Se la domanda è generica (tipo 'Ciao, come stai?'), rispondi breve.
307
  5) Se trovi risultati, la risposta finale deve essere la query SPARQL (una sola riga).
308
  6) Se non trovi nulla, rispondi con 'Nessuna info.'
309
+ 7) Non multiline. Esempio: PREFIX base: <...> SELECT ?x WHERE { ... }.
310
 
311
+ **Esempi:**
312
  Utente: Chi ha creato l'opera 'Amore e Psiche'?
313
+ Risposta: PREFIX base: <http://www.semanticweb.org/lucreziamosca/ontologies/progettoMuseo#> SELECT ?creatore WHERE { ?opera base:hasName "Amore e Psiche" . ?opera base:creatoDa ?creatore . }
314
+
315
+ Utente: che ore sono?
316
+ Risposta: Ciao! Sono un assistente museale e non ho informazioni sulle ore attuali. Ti consiglio di consultare un orologio o un dispositivo mobile per conoscere l'ora esatta.
317
 
318
+ ### FINE Regole Stringenti ###
319
  """
320
 
321
  def create_explanation_prompt(results_str: str) -> str:
 
326
  Ora fornisci una breve spiegazione museale (massimo ~10 righe), senza inventare oltre i risultati.
327
  """
328
 
329
+ async def call_hf_model(prompt: str, temperature: float = 0.5, max_tokens: int = 150) -> str:
330
  """Chiama il modello Hugging Face tramite l'API REST e gestisce la risposta."""
331
  logger.debug("Chiamo HF con il seguente prompt:")
332
  content_preview = (prompt[:300] + '...') if len(prompt) > 300 else prompt
333
  logger.debug(f"PROMPT => {content_preview}")
334
+
335
  headers = {
336
  "Authorization": f"Bearer {API_KEY}"
337
  }
 
343
  "top_p": 0.9
344
  }
345
  }
346
+
347
  try:
348
  response = requests.post(
349
  f"https://api-inference.huggingface.co/models/{HF_MODEL}",
 
361
  raw = data["generated_text"]
362
  else:
363
  raise ValueError("Nessun campo 'generated_text' nella risposta.")
364
+
365
  # Forza la risposta su una singola linea se multilinea
366
  single_line = " ".join(raw.splitlines())
367
  logger.debug(f"Risposta HF single-line: {single_line}")
 
370
  logger.error(f"Errore nella chiamata all'API Hugging Face tramite requests: {e}")
371
  raise HTTPException(status_code=500, detail=str(e))
372
 
373
+ def is_ontology_related(query: str) -> bool:
374
+ """Determina se la domanda è pertinente all'ontologia."""
375
+ keywords = ["opera", "museo", "stanza", "tour", "visitatore", "biglietto", "guida", "evento", "agente"]
376
+ return any(keyword.lower() in query.lower() for keyword in keywords)
377
+
378
  # Prepara i file necessari per RAG
379
  prepare_retrieval()
380
 
 
385
 
386
  class QueryRequest(BaseModel):
387
  message: str
388
+ max_tokens: int = 150 # Ridotto per risposte concise
389
  temperature: float = 0.5
390
 
391
  @app.post("/generate-response/")
 
393
  user_input = req.message
394
  logger.info(f"Utente dice: {user_input}")
395
 
396
+ if not is_ontology_related(user_input):
397
+ return {
398
+ "type": "NATURAL",
399
+ "response": "Ciao! Sono un assistente museale e non ho informazioni sulle ore attuali. Ti consiglio di consultare un orologio o un dispositivo mobile per conoscere l'ora esatta."
400
+ }
401
+
402
  try:
403
  # Recupera documenti rilevanti usando RAG
404
  retrieved_docs = retrieve_relevant_documents(user_input, top_k=3)