Update app.py
Browse files
app.py
CHANGED
@@ -6,6 +6,10 @@ from fastapi import FastAPI, HTTPException
|
|
6 |
import rdflib
|
7 |
from rdflib import RDF, RDFS, OWL
|
8 |
from huggingface_hub import InferenceClient
|
|
|
|
|
|
|
|
|
9 |
|
10 |
logging.basicConfig(
|
11 |
level=logging.DEBUG,
|
@@ -27,6 +31,17 @@ HF_MODEL = "Qwen/Qwen2.5-72B-Instruct"
|
|
27 |
MAX_CLASSES = 30
|
28 |
MAX_PROPERTIES = 30
|
29 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
|
31 |
def extract_classes_and_properties(rdf_file:str) -> str:
|
32 |
"""
|
@@ -69,33 +84,28 @@ def extract_classes_and_properties(rdf_file:str) -> str:
|
|
69 |
summary = f"""\
|
70 |
# CLASSI (max {MAX_CLASSES})
|
71 |
{txt_classes}
|
72 |
-
|
73 |
# PROPRIETA' (max {MAX_PROPERTIES})
|
74 |
{txt_props}
|
75 |
"""
|
76 |
return summary
|
77 |
|
78 |
-
|
79 |
knowledge_text = extract_classes_and_properties(RDF_FILE)
|
80 |
|
81 |
-
|
82 |
-
def create_system_message(ont_text:str)->str:
|
83 |
"""
|
84 |
-
Prompt di sistema robusto, con regole su query in una riga
|
85 |
-
|
86 |
-
una proprietà simile a 'base:materialeOpera' o analoga, ma NON tassativo.
|
87 |
"""
|
88 |
return f"""
|
89 |
Sei un assistente museale. Ecco un estratto di CLASSI e PROPRIETA' dell'ontologia (senza NamedIndividuals):
|
90 |
-
|
91 |
--- ONTOLOGIA ---
|
92 |
{ont_text}
|
93 |
--- FINE ---
|
94 |
-
|
|
|
95 |
Suggerimento: se l'utente chiede il 'materiale' di un'opera, potresti usare qualcosa come
|
96 |
'base:materialeOpera' o un'altra proprietà simile (se esiste). Non è tassativo: usa
|
97 |
la proprietà che ritieni più affine se ci sono riferimenti in ontologia.
|
98 |
-
|
99 |
REGOLE STRINGENTI:
|
100 |
1) Se l'utente chiede info su questa ontologia, genera SEMPRE una query SPARQL in UNA SOLA RIGA,
|
101 |
con prefix:
|
@@ -104,21 +114,17 @@ REGOLE STRINGENTI:
|
|
104 |
3) Se la domanda è generica (tipo 'Ciao, come stai?'), rispondi breve.
|
105 |
4) Se trovi risultati, risposta finale = la query SPARQL (una sola riga).
|
106 |
5) Se non trovi nulla, di' 'Nessuna info.'
|
107 |
-
6) Non multiline. Esempio:
|
108 |
-
|
109 |
FINE REGOLE
|
110 |
"""
|
111 |
|
112 |
-
|
113 |
def create_explanation_prompt(results_str:str)->str:
|
114 |
return f"""
|
115 |
Ho ottenuto questi risultati SPARQL:
|
116 |
{results_str}
|
117 |
-
|
118 |
Ora fornisci una breve spiegazione museale (massimo ~10 righe), senza inventare oltre i risultati.
|
119 |
"""
|
120 |
|
121 |
-
|
122 |
async def call_hf_model(messages, temperature=0.5, max_tokens=1024)->str:
|
123 |
logger.debug("Chiamo HF con i seguenti messaggi:")
|
124 |
for m in messages:
|
@@ -140,9 +146,6 @@ async def call_hf_model(messages, temperature=0.5, max_tokens=1024)->str:
|
|
140 |
logger.error(f"HuggingFace error: {e}")
|
141 |
raise HTTPException(status_code=500, detail=str(e))
|
142 |
|
143 |
-
|
144 |
-
from fastapi import FastAPI
|
145 |
-
|
146 |
app=FastAPI()
|
147 |
|
148 |
class QueryRequest(BaseModel):
|
@@ -155,7 +158,11 @@ async def generate_response(req:QueryRequest):
|
|
155 |
user_input=req.message
|
156 |
logger.info(f"Utente dice: {user_input}")
|
157 |
|
158 |
-
|
|
|
|
|
|
|
|
|
159 |
msgs=[
|
160 |
{"role":"system","content":sys_msg},
|
161 |
{"role":"user","content":user_input}
|
@@ -182,7 +189,6 @@ async def generate_response(req:QueryRequest):
|
|
182 |
sparql_query=r1
|
183 |
|
184 |
# Esegui la query con rdflib
|
185 |
-
import rdflib
|
186 |
g=rdflib.Graph()
|
187 |
try:
|
188 |
g.parse(RDF_FILE,format="xml")
|
|
|
6 |
import rdflib
|
7 |
from rdflib import RDF, RDFS, OWL
|
8 |
from huggingface_hub import InferenceClient
|
9 |
+
from sentence_transformers import SentenceTransformer
|
10 |
+
import faiss
|
11 |
+
import json
|
12 |
+
import numpy as np
|
13 |
|
14 |
logging.basicConfig(
|
15 |
level=logging.DEBUG,
|
|
|
31 |
MAX_CLASSES = 30
|
32 |
MAX_PROPERTIES = 30
|
33 |
|
34 |
+
# Carica i documenti e l'indice FAISS
|
35 |
+
with open("data/documents.json", "r", encoding="utf-8") as f:
|
36 |
+
documents = json.load(f)
|
37 |
+
index = faiss.read_index("data/faiss.index")
|
38 |
+
model = SentenceTransformer('all-MiniLM-L6-v2')
|
39 |
+
|
40 |
+
def retrieve_relevant_documents(query: str, top_k: int = 5):
|
41 |
+
query_embedding = model.encode([query], convert_to_numpy=True)
|
42 |
+
distances, indices = index.search(query_embedding, top_k)
|
43 |
+
relevant_docs = [documents[idx] for idx in indices[0]]
|
44 |
+
return relevant_docs
|
45 |
|
46 |
def extract_classes_and_properties(rdf_file:str) -> str:
|
47 |
"""
|
|
|
84 |
summary = f"""\
|
85 |
# CLASSI (max {MAX_CLASSES})
|
86 |
{txt_classes}
|
|
|
87 |
# PROPRIETA' (max {MAX_PROPERTIES})
|
88 |
{txt_props}
|
89 |
"""
|
90 |
return summary
|
91 |
|
|
|
92 |
knowledge_text = extract_classes_and_properties(RDF_FILE)
|
93 |
|
94 |
+
def create_system_message(ont_text:str, retrieved_docs:str)->str:
|
|
|
95 |
"""
|
96 |
+
Prompt di sistema robusto, con regole su query in una riga e
|
97 |
+
informazioni recuperate tramite RAG.
|
|
|
98 |
"""
|
99 |
return f"""
|
100 |
Sei un assistente museale. Ecco un estratto di CLASSI e PROPRIETA' dell'ontologia (senza NamedIndividuals):
|
|
|
101 |
--- ONTOLOGIA ---
|
102 |
{ont_text}
|
103 |
--- FINE ---
|
104 |
+
Ecco alcune informazioni rilevanti recuperate dalla base di conoscenza:
|
105 |
+
{retrieved_docs}
|
106 |
Suggerimento: se l'utente chiede il 'materiale' di un'opera, potresti usare qualcosa come
|
107 |
'base:materialeOpera' o un'altra proprietà simile (se esiste). Non è tassativo: usa
|
108 |
la proprietà che ritieni più affine se ci sono riferimenti in ontologia.
|
|
|
109 |
REGOLE STRINGENTI:
|
110 |
1) Se l'utente chiede info su questa ontologia, genera SEMPRE una query SPARQL in UNA SOLA RIGA,
|
111 |
con prefix:
|
|
|
114 |
3) Se la domanda è generica (tipo 'Ciao, come stai?'), rispondi breve.
|
115 |
4) Se trovi risultati, risposta finale = la query SPARQL (una sola riga).
|
116 |
5) Se non trovi nulla, di' 'Nessuna info.'
|
117 |
+
6) Non multiline. Esempio: PREFIX base: <...> SELECT ?x WHERE { ... }.
|
|
|
118 |
FINE REGOLE
|
119 |
"""
|
120 |
|
|
|
121 |
def create_explanation_prompt(results_str:str)->str:
|
122 |
return f"""
|
123 |
Ho ottenuto questi risultati SPARQL:
|
124 |
{results_str}
|
|
|
125 |
Ora fornisci una breve spiegazione museale (massimo ~10 righe), senza inventare oltre i risultati.
|
126 |
"""
|
127 |
|
|
|
128 |
async def call_hf_model(messages, temperature=0.5, max_tokens=1024)->str:
|
129 |
logger.debug("Chiamo HF con i seguenti messaggi:")
|
130 |
for m in messages:
|
|
|
146 |
logger.error(f"HuggingFace error: {e}")
|
147 |
raise HTTPException(status_code=500, detail=str(e))
|
148 |
|
|
|
|
|
|
|
149 |
app=FastAPI()
|
150 |
|
151 |
class QueryRequest(BaseModel):
|
|
|
158 |
user_input=req.message
|
159 |
logger.info(f"Utente dice: {user_input}")
|
160 |
|
161 |
+
# Recupera documenti rilevanti usando RAG
|
162 |
+
relevant_docs = retrieve_relevant_documents(user_input, top_k=3)
|
163 |
+
retrieved_text = "\n".join([doc['text'] for doc in relevant_docs])
|
164 |
+
|
165 |
+
sys_msg=create_system_message(knowledge_text, retrieved_text)
|
166 |
msgs=[
|
167 |
{"role":"system","content":sys_msg},
|
168 |
{"role":"user","content":user_input}
|
|
|
189 |
sparql_query=r1
|
190 |
|
191 |
# Esegui la query con rdflib
|
|
|
192 |
g=rdflib.Graph()
|
193 |
try:
|
194 |
g.parse(RDF_FILE,format="xml")
|