mirror of
https://github.com/Alkatrazz24/Funk-lab.git
synced 2026-07-08 05:34:43 +02:00
funk-docs utilisait le modèle de CHAT qwen3-8b (:1234, 4096 dim) comme embedder → similarités quasi indiscernables (tous les scores ~0.96, ranking médiocre). Bascule sur l'instance dédiée nomic-embed-text (:1238, 768 dim) — la même que la mémoire STT — déjà identifiée comme roadmap dans le README. rag-ingest ET rag-query alignés (même modèle/dim). Seuil rag-query abaissé 0.60→0.40 (nomic étale les scores plus bas). Collection recréée en 768. Mesure : scores désormais étalés 0.61-0.74, et les bons docs ressortent en tête (dnsmasq→dnsmasq.md, nftables→phase gateway, wedge→llama_server.md). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
79 lines
2.4 KiB
Python
79 lines
2.4 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Interroge la base vectorielle Qdrant avec une question en langage naturel.
|
|
Usage: rag-query "ma question" [--top N]
|
|
Retourne les passages de documentation les plus pertinents.
|
|
"""
|
|
import sys
|
|
import json
|
|
import os
|
|
import urllib.request
|
|
|
|
QDRANT_URL = os.environ.get("QDRANT_URL", "http://127.0.0.1:6333")
|
|
# Doit matcher rag-ingest : embedder dédié nomic-embed-text (gpu-01 :1238, dim 768).
|
|
EMBED_URL = os.environ.get("EMBED_URL", "http://192.168.10.20:1238/v1/embeddings")
|
|
EMBED_MODEL = os.environ.get("EMBED_MODEL", "nomic-embed-text")
|
|
COLLECTION = os.environ.get("RAG_COLLECTION", "funk-docs")
|
|
# nomic étale les scores cosinus plus bas que qwen3-8b (qui saturait ~0.96) → seuil
|
|
# plus permissif pour ne pas écarter de bons passages. Surchargeable via RAG_MIN_SCORE.
|
|
MIN_SCORE = float(os.environ.get("RAG_MIN_SCORE", "0.40"))
|
|
|
|
|
|
def _post(url, data):
|
|
req = urllib.request.Request(
|
|
url,
|
|
data=json.dumps(data).encode(),
|
|
headers={"Content-Type": "application/json"},
|
|
method="POST"
|
|
)
|
|
with urllib.request.urlopen(req, timeout=60) as r:
|
|
return json.loads(r.read())
|
|
|
|
|
|
def embed(text):
|
|
result = _post(EMBED_URL, {"model": EMBED_MODEL, "input": text})
|
|
return result["data"][0]["embedding"]
|
|
|
|
|
|
def search(question, top=5):
|
|
vector = embed(question)
|
|
result = _post(f"{QDRANT_URL}/collections/{COLLECTION}/points/search", {
|
|
"vector": vector,
|
|
"limit": top,
|
|
"with_payload": True,
|
|
"score_threshold": MIN_SCORE,
|
|
})
|
|
return result["result"]
|
|
|
|
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) < 2:
|
|
print("Usage: rag-query \"question\" [--top N]", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
question = sys.argv[1]
|
|
top = 5
|
|
if "--top" in sys.argv:
|
|
idx = sys.argv.index("--top")
|
|
try:
|
|
top = int(sys.argv[idx + 1])
|
|
except (IndexError, ValueError):
|
|
pass
|
|
|
|
try:
|
|
results = search(question, top)
|
|
except Exception as e:
|
|
print(f"ERREUR: {e}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
if not results:
|
|
print(f"Aucun résultat pertinent pour : {question}")
|
|
sys.exit(0)
|
|
|
|
print(f"=== Contexte RAG — {len(results)} résultat(s) pour : {question} ===\n")
|
|
for i, r in enumerate(results, 1):
|
|
p = r["payload"]
|
|
score = r["score"]
|
|
print(f"--- [{i}] {p['file']} § {p['section']} (score: {score:.3f}) ---")
|
|
print(p["text"][:700])
|
|
print()
|