File size: 5,977 Bytes
ac52c4d 89f944e ac52c4d 89f944e ac52c4d 2ecaeab 89f944e 2ecaeab 89f944e 2ecaeab ac52c4d 89f944e ac52c4d 89f944e ac52c4d 89f944e ac52c4d 89f944e ac52c4d 89f944e ac52c4d 69d6e40 89f944e ac52c4d 89f944e ac52c4d 89f944e ac52c4d 89f944e 4c13256 89f944e 69d6e40 3aa01bd 89f944e 69d6e40 89f944e 059bef5 89f944e ac52c4d 89f944e ac52c4d 2ecaeab ac52c4d 89f944e ac52c4d 89f944e ac52c4d 2ecaeab ac52c4d 89f944e 69d6e40 ac52c4d 89f944e ac52c4d 89f944e 2ecaeab 3aa01bd 89f944e ac52c4d 3aa01bd 89f944e 69d6e40 89f944e 69d6e40 89f944e ac52c4d 89f944e |
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 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
import os
import logging
from typing import Optional
from pydantic import BaseModel
from fastapi import FastAPI, HTTPException
from huggingface_hub import InferenceClient
import rdflib
# Configurazione logging
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[
logging.FileHandler("app.log"),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
# =========================================
# PARAMETRI BASE
# =========================================
API_KEY = os.getenv("HF_API_KEY")
if not API_KEY:
logger.error("HF_API_KEY non impostata nell'ambiente.")
raise EnvironmentError("HF_API_KEY non impostata nell'ambiente.")
# Nome del file RDF
RDF_FILE = "Ontologia.rdf"
# Inizializza il client
client = InferenceClient(api_key=API_KEY)
# =========================================
# Carica e Preprocessa l'Ontologia
# =========================================
def load_ontology_as_text(rdf_file: str, max_triples: int = 300) -> str:
"""
Legge l'ontologia dal file RDF e costruisce
una stringa "knowledge_text" con un numero limitato di triple,
per non sforare i limiti di contesto.
max_triples limita quante triple includere.
"""
if not os.path.exists(rdf_file):
return "Nessun file RDF trovato."
g = rdflib.Graph()
try:
g.parse(rdf_file, format="xml")
except Exception as e:
logger.error(f"Errore parsing RDF: {e}")
return "Errore parsing RDF."
# Convertiamo un certo numero di triple in stringa
knowledge_lines = []
count = 0
for s, p, o in g:
# Troncamento se necessario (evita di incollare 10.000 triple)
if count >= max_triples:
break
# Abbreviamo s, p, o se sono lunghi
s_str = str(s)[:200]
p_str = str(p)[:200]
o_str = str(o)[:200]
line = f"- {s_str} | {p_str} | {o_str}"
knowledge_lines.append(line)
count += 1
knowledge_text = "\n".join(knowledge_lines)
return knowledge_text
# Carichiamo e preprocessiamo la knowledge
knowledge_text = load_ontology_as_text(RDF_FILE, max_triples=600) # Aumenta/diminuisci se serve
# =========================================
# Creazione Prompt di Sistema
# =========================================
def create_system_message(knowledge_text: str) -> str:
"""
Crea un prompt di sistema con l'ontologia (o una versione ridotta) inline,
obbligando il modello a cercare le info per la query SPARQL da qui.
"""
# Nota: qui rafforziamo che deve SEMPRE consultare questi triple.
# E deve generare query SPARQL SOLO su questi triple, etc.
system_message = f"""
Sei un assistente per un museo. Qui hai l'estratto dell'ontologia RDF in formato triple:
(EntitaSoggetto, EntitaProprieta, EntitaOggetto).
Devi usare ESCLUSIVAMENTE queste informazioni come knowledge base.
Ecco la knowledge in forma di triple (max 600):
{knowledge_text}
REGOLE TASSATIVE:
1. Se la domanda dell'utente richiede info su entità/opere ecc., DEVI generare una query SPARQL che si basa su questi triple.
2. Se la domanda è chat semplice (saluto, ecc.), puoi rispondere brevemente, ma se serve una knowledge, la cerchi nei triple.
3. Se i triple non contengono l'info, di' che non è disponibile.
4. Il prefisso da usare:
PREFIX base: <http://www.semanticweb.org/lucreziamosca/ontologies/progettoMuseo#>
5. Se hai generato la query e bisogna interpretare i risultati (es. autoreOpera?), puoi fornire la risposta.
Ma l'info deve derivare dai triple sopra.
Attenzione al fatto che le stringhe (es. <nomeOpera> ) possono avere @it o differenze minuscole/maiuscole.
Puoi usare FILTER( STR(?x) = "valore" ) se serve.
Buona fortuna!
"""
return system_message
# =========================================
# Funzione che chiama il modello
# =========================================
async def call_model(messages, temperature=0.7, max_tokens=2048):
logger.info("Chiamata al modello iniziata.")
try:
response = client.chat.completions.create(
model="Qwen/Qwen2.5-72B-Instruct",
messages=messages,
temperature=temperature,
max_tokens=max_tokens,
top_p=0.9, # puoi settare come vuoi
stream=False
)
raw_text = response["choices"][0]["message"]["content"]
logger.debug(f"Risposta del modello: {raw_text}")
# Togli newline
return raw_text.replace("\n", " ").strip()
except Exception as e:
logger.error(f"Errore durante la chiamata al modello: {e}")
raise HTTPException(status_code=500, detail=str(e))
# =========================================
# FASTAPI
# =========================================
from fastapi import FastAPI
app = FastAPI()
class QueryRequest(BaseModel):
message: str
max_tokens: int = 1024
temperature: float = 0.5
@app.post("/generate-response/")
async def generate_response(req: QueryRequest):
user_msg = req.message
logger.info(f"Ricevuta richiesta: {user_msg}")
# 1) Creiamo un system_msg che contiene TUTTI i triple (preprocessati)
system_msg = create_system_message(knowledge_text)
messages = [
{"role": "system", "content": system_msg},
{"role": "user", "content": user_msg}
]
# 2) Chiamiamo il modello
response_text = await call_model(
messages,
temperature=req.temperature,
max_tokens=req.max_tokens
)
logger.info(f"Risposta generata dal modello: {response_text}")
# (Opzionale) Se vuoi, qui potresti controllare se la risposta inizia con "PREFIX base:"
# e se sì, eseguire su un Graph locale la query, interpretarla, ecc.
# Ma in questo esempio, ci fermiamo e inviamo la raw response:
return {"type": "NATURAL", "response": response_text}
@app.get("/")
async def root():
return {"message": "Server con ontologia in prompt di sistema!"}
|