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!"}