- A: gate store con cross-encoder — guardrail.decide async, conferma/scarta quasi-duplicati (CROSS_DUP_CONFIRMED/WEAK/LOW_COSINE), suggerimento supersedes in WARN, degrada a cosine-only se il reranker è giù - B: verifica supersede — cross-score (nuovo,vecchio) sotto soglia → supersede_warning non bloccante + audit - C: score composito in search — rerank + importance (nuovo campo payload) + recency decay (180gg) + authority, pesi SCORE_W_* da env - E: multi-query — SearchIn.queries (max 3), pool unito con dedup, rerank unico; endpoint POST /v1/score come primitiva cross-encoder (F-lite) - extension search.ts: param queries + rerank_score/composite in output - D: scripts/consolidate.py — dedup periodico a coppie via cross-encoder con report ntfy e --apply via gateway - test: 69 pass (+11 strategie); guardrail_version similarity-v2
Memory Gateway — deploy
Componente server di pi-qmem: FastAPI + Qdrant 1.19 + Ollama (BGE-M3). Nessun LLM in scrittura: l'agente salva record deliberati e strutturati.
pi (estensione pi-qmem) ──HTTPS/VPN──▶ Memory Gateway (FastAPI:8082) ──▶ Qdrant 1.19 (6333)
│
└──▶ Ollama BGE-M3 (11434, nativo host)
Deploy (Docker Compose)
# 1. Prepara l'ambiente (vedi qmem-gateway/docker-compose.yml come riferimento)
cp .env.example .env
chmod 600 .env
# genera le chiavi:
# QDRANT_ADMIN_API_KEY=$(openssl rand -hex 32)
# QDRANT_READ_ONLY_API_KEY=$(openssl rand -hex 32)
# API_KEYS=$(openssl rand -hex 32) # chiave condivisa per gli agenti
# 2. Avvia
docker compose up -d --build
# 3. Verifica
curl http://127.0.0.1:8082/v1/status
Requisiti: Docker + Compose v2, Ollama con modello bge-m3 sul host
(ollama pull bge-m3), porta 8082 libera sull'interfaccia VPN.
API
| Endpoint | Descrizione |
|---|---|
POST /v1/memories |
Crea record (text, kind, agent_id, scope, project_id obbligatorio, source, expires_at, supersedes_id, supersede_reason). Applica il guardrail di similarità pre-scrittura |
POST /v1/memories:search |
Ricerca semantica (query, kind, project_id, scope, top_k, include_superseded, min_score) |
GET /v1/memories/{id} |
Recupera per UUID |
DELETE /v1/memories/{id} |
Elimina per UUID |
GET /v1/meta/overview |
Discovery: scope×kind, progetti, agenti, superseduti (cache 60s) |
GET /v1/status |
Health + statistiche |
Auth: header X-API-Key (chiave condivisa, accesso completo). Rate limit 120 req/min per chiave. Audit log in JSON lines (docker logs).
Versione del codice
GET /v1/version (pubblico) espone la versione del codice in esecuzione, inclusa l'hash del commit Git da cui è stato costruito il container:
{"version": "2.7.0", "git_commit": "eccb2cb...", "guardrail_version": "similarity-v1", ...}
Anche GET /v1/status include version, git_commit e guardrail_version. L'hash è iniettato al build via ARG GIT_COMMIT/ENV GIT_COMMIT nel Dockerfile (default unknown). Per costruire con l'hash:
docker compose build --build-arg GIT_COMMIT=$(git rev-parse HEAD) gateway
# o nel compose: build: { context: ./gateway, args: { GIT_COMMIT: ${GIT_COMMIT:-unknown} } }
Guardrail di similarità (v1)
Enforcement deterministico FUORI dall'LLM, prima di ogni scrittura su POST /v1/memories:
- Strato 1 — hash esatto: SHA-256 del testo normalizzato (
text_hashnel payload). Se esiste un record attivo con lo stesso hash →409 BLOCK (EXACT_DUPLICATE). - Strato 2 — similarità semantica top-3: embedding BGE-M3 cosine sui record attivi (esclusi i superseded).
- top-1 ≥
GUARDRAIL_BLOCK_THRESHOLD(default 0.85) →409 BLOCK (KNOWN_SOLUTION) - top-1 ≥
GUARDRAIL_WARN_THRESHOLD(default 0.70) →WARN: salva con flagguardrailnel payload - altrimenti →
ALLOW
- top-1 ≥
Il supersede esplicito (supersedes_id) è una correzione intenzionale: bypassa il guardrail.
Configurazione (env): GUARDRAIL_ENABLED (default true), GUARDRAIL_BLOCK_THRESHOLD, GUARDRAIL_WARN_THRESHOLD. Soglie di partenza da calibrare sul corpus reale.
Risposta BLOCK (409):
{"detail": {"error": "duplicate_memory", "reason": "KNOWN_SOLUTION", "matches": [{"memory_id": "...", "score": 0.92}], "message": "..."}}
Sicurezza
- Qdrant bindato su 127.0.0.1; gateway solo su interfaccia VPN
- Chiavi in
.env(0600), mai committate - JWT RBAC su Qdrant (admin + read-only)
- Backup: snapshot Qdrant + rotazione 7 giorni (cron:
0 3 * * * /opt/memory/backup.sh)
Dettagli operativi
Procedure complete (teardown, restore, nginx, troubleshooting): vedi
docs/playbook.md nel repo pi-qmem.