File size: 5,814 Bytes
ee1f7e6 034a1e6 ee1f7e6 034a1e6 ee1f7e6 4c52594 1575481 034a1e6 21119e3 034a1e6 70bec3c 034a1e6 a1f8fd1 70bec3c 034a1e6 8c772f5 4c52594 034a1e6 70bec3c c191446 034a1e6 70bec3c 3031ece 034a1e6 e623027 c191446 70bec3c c191446 baa2201 c191446 70bec3c c191446 70bec3c 034a1e6 baa2201 034a1e6 3b0ec68 ee1f7e6 034a1e6 70bec3c 034a1e6 70bec3c aa07e9d 034a1e6 70bec3c 1575481 034a1e6 70bec3c |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
import json
import os
from pathlib import Path
from huggingface_hub import InferenceClient
from rdflib import Graph
from pydantic import BaseModel
from fastapi import FastAPI, HTTPException
import logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
logger = logging.getLogger(__name__)
API_KEY = os.getenv("HF_API_KEY")
client = InferenceClient(api_key=API_KEY)
RDF_FILE = "Ontologia.rdf"
# Funzione di validazione
def validate_sparql_query(query: str, rdf_file_path: str) -> bool:
g = Graph()
try:
g.parse(rdf_file_path, format="xml")
g.query(query) # Se c'è un errore di sintassi o referenza, solleva eccezione
return True
except Exception as e:
logger.error(f"Errore durante la validazione della query SPARQL: {e}")
return False
# Prompt di sistema "stringente"
def create_system_message(rdf_context: str) -> str:
"""
Prompt di sistema estremamente rigido, che forza il modello
a usare solo il prefisso definito e a generare la query in un'unica riga.
"""
return f"""
Sei un assistente esperto nella generazione di query SPARQL basate su un'ontologia RDF.
Ecco un riassunto dell'ontologia su cui devi lavorare:
{rdf_context}
DI SEGUITO LE REGOLE TASSATIVE:
1. DEVI usare ESCLUSIVAMENTE questo prefisso di base (e NON modificarlo in nessun modo):
PREFIX base: <http://www.semanticweb.org/lucreziamosca/ontologies/2024/11/untitled-ontology-39/>
2. La query deve stare in UNA SOLA RIGA, senza andare a capo.
3. La query deve INIZIARE con:
PREFIX base: <http://www.semanticweb.org/lucreziamosca/ontologies/2024/11/untitled-ontology-39/> SELECT
oppure
PREFIX base: <http://www.semanticweb.org/lucreziamosca/ontologies/2024/11/untitled-ontology-39/> ASK
4. Se devi indicare una classe, usa: ?qualcosa a base:NomeClasse .
5. Se devi indicare una proprietà, usa: ?s base:NomeProprieta ?o .
6. NON generare alcun altro prefisso.
7. NON utilizzare URI lunghe senza < > e NON inventare prefissi o risorse inesistenti.
8. Se non puoi rispondere con una query SPARQL valida secondo questi criteri, scrivi:
"Non posso generare una query SPARQL per questa richiesta."
Esempio di query corretta (fittizia) in una sola riga:
PREFIX base: <http://www.semanticweb.org/lucreziamosca/ontologies/2024/11/untitled-ontology-39/> SELECT ?stanza WHERE { ?stanza a base:Stanza . } LIMIT 10
RISPONDI ESCLUSIVAMENTE CON LA QUERY O IL MESSAGGIO DI IMPOSSIBILITA'.
NON SCRIVERE NESSUN COMMENTO AGGIUNTIVO.
"""
# Funzione per chiamare il modello su Hugging Face
async def call_model(messages, temperature=0.7, max_tokens=2048):
try:
response = client.chat.completions.create(
model="Qwen/Qwen2.5-72B-Instruct",
messages=messages,
temperature=temperature,
max_tokens=max_tokens,
top_p=0.7,
stream=False
)
raw_text = response["choices"][0]["message"]["content"]
return raw_text.replace("\n", " ").strip()
except Exception as e:
logger.error(f"Errore nel modello: {e}")
raise HTTPException(status_code=500, detail=str(e))
# Caricamento di un riassunto dell'ontologia (semplificato per brevità)
def load_rdf_summary() -> str:
# Qui un caricamento minimo o un testo statico
return "Classi e proprietà dell'ontologia: base:Stanza, base:Contiene, base:Opera, ecc."
app = FastAPI()
rdf_context = load_rdf_summary()
class QueryRequest(BaseModel):
message: str
max_tokens: int = 2048
temperature: float = 0.7
@app.post("/generate-query/")
async def generate_query(request: QueryRequest):
# 1) Prima iterazione
system_msg = create_system_message(rdf_context)
user_msg = request.message
messages = [
{"role": "system", "content": system_msg},
{"role": "user", "content": user_msg},
]
response1 = await call_model(messages, request.temperature, request.max_tokens)
logger.info(f"[Prima iterazione] Risposta generata dal modello: {response1}")
# 2) Validazione
if not (response1.startswith("PREFIX") and ("SELECT" in response1 or "ASK" in response1)):
# Fallimento immediato
return {"query": None, "explanation": "La query non rispetta le regole base (PREFIX + SELECT/ASK)."}
if validate_sparql_query(response1, RDF_FILE):
# Query valida! Restituisco la prima
return {"query": response1, "explanation": "Query valida alla prima iterazione."}
else:
# 3) Seconda iterazione “correttiva”
correction_msg = create_correction_message(rdf_context, "Errore di validazione (prima iterazione).")
messages2 = [
{"role": "system", "content": system_msg}, # Sistema invariato
{"role": "assistant", "content": response1}, # Metti la risposta errata in contesto
{"role": "system", "content": correction_msg}, # Istruzione di correzione
]
response2 = await call_model(messages2, request.temperature, request.max_tokens)
logger.info(f"[Seconda iterazione] Risposta generata dal modello: {response2}")
if not (response2.startswith("PREFIX") and ("SELECT" in response2 or "ASK" in response2)):
return {"query": None, "explanation": "Anche la seconda query non rispetta le regole (PREFIX + SELECT/ASK)."}
if validate_sparql_query(response2, RDF_FILE):
return {"query": response2, "explanation": "Query valida alla seconda iterazione (corretta)."}
else:
return {
"query": None,
"explanation": "Anche la seconda iterazione ha prodotto una query non valida. Interrompo."
}
@app.get("/")
async def root():
return {"message": "Server attivo per generare query SPARQL"}
|