Add pgvector quality exporter + fix missing os import
This commit is contained in:
94
exporters/pgvector_quality_exporter.py
Normal file
94
exporters/pgvector_quality_exporter.py
Normal file
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
pgvector quality exporter
|
||||
Mesure la pertinence des recherches vectorielles pgvector sur Mem0.
|
||||
Exporte la distribution des distances comme histogramme Prometheus
|
||||
|
||||
Usage dans le workflow mem0:
|
||||
from exporters.pgvector_quality import log_similarity_score
|
||||
|
||||
results = db.query(query) # SQL avec pgvector <=> operator
|
||||
if results:
|
||||
best_distance = results[0].distance
|
||||
log_similarity_score(best_distance, query_type='mem0_search')
|
||||
"""
|
||||
|
||||
import os
|
||||
import psycopg2
|
||||
import json
|
||||
from prometheus_client import start_http_server, Histogram, Gauge, Counter
|
||||
import logging
|
||||
|
||||
|
||||
logger = logging.getLogger('pgvector_quality_monitor')
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s')
|
||||
|
||||
# Metrics Prometheus
|
||||
SIMILARITY_SCORE_HIST = Histogram(
|
||||
'pgvector_similarity_score',
|
||||
'Score similarite du meilleur resultat (pgvector <=> operator)',
|
||||
['query_type'], # mem0_search, text_search, etc.
|
||||
namespace='aurelie'
|
||||
)
|
||||
|
||||
BEST_RESULT_GAUGE = Gauge(
|
||||
'pgvector_best_result_distance',
|
||||
'Distance du meilleur resultat pgvector',
|
||||
['query_type'],
|
||||
namespace='aurelie'
|
||||
)
|
||||
|
||||
QUERY_COUNT = Counter(
|
||||
'pgvector_query_count_total',
|
||||
'Nombre total de requetes pgvector executees',
|
||||
['status'], # success, error, no_results
|
||||
namespace='aurelie'
|
||||
)
|
||||
|
||||
|
||||
def measure_similarity(query_text: str, threshold=0.1) -> dict:
|
||||
"""Execute une similarity search et retourne le meilleur score."""
|
||||
try:
|
||||
conn = psycopg2.connect(
|
||||
dbname="aurelie", # PG database mem0 (config à adapter selon l'environnement)
|
||||
host=os.getenv('PGHOST', 'localhost'),
|
||||
port=os.getenv('PGPORT', '5432')
|
||||
)
|
||||
cur = conn.cursor()
|
||||
|
||||
# pgvector <=> operator → cosine distance (0=identique, 2=complètement oppose)
|
||||
query = """\\
|
||||
SELECT id::text, content,
|
||||
(embedding <=> %s::vector) AS distance
|
||||
FROM memory_store
|
||||
ORDER BY distance ASC
|
||||
LIMIT 5;"""
|
||||
|
||||
cur.execute(query, [query_text])
|
||||
results = cur.fetchall()
|
||||
|
||||
if results:
|
||||
best_score = results[0][-1] # le champ 'distance' (dernier de la tuple)
|
||||
SIMILARITY_SCORE_HIST.labels(query_type='mem0_search').observe(best_score)
|
||||
BEST_RESULT_GAUGE.labels(query_type='mem0_search').set(best_score)
|
||||
|
||||
QUERY_COUNT.labels(status='success').inc()
|
||||
|
||||
# Si le meilleur score est > threshold → alerte (ma memoire contextuelle devient floue)
|
||||
if best_score > threshold: # 0.1 = seuil de "pertinence faible"
|
||||
logger.warning(f"Score trop bas: {best_score:.3f} — ma memoire contextuelle est peu pertinente")
|
||||
|
||||
return {'best_score': float(best_score), 'results_count': len(results)}
|
||||
else:
|
||||
QUERY_COUNT.labels(status='no_results').inc()
|
||||
return {'best_score': None, 'results_count': 0}
|
||||
|
||||
except psycopg2.Error as e:
|
||||
logger.error(f"Erreur pgvector similarity search: {e}")
|
||||
QUERY_COUNT.labels(status='error').inc()
|
||||
raise
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
start_http_server(8105) # Prometheus scrape sur port 8105
|
||||
logger.info('pgvector quality exporter started on :8105')
|
||||
Reference in New Issue
Block a user