Files
pi-qmem/README.md
T
Matteo Benedetto 1832562a7f feat: indice locale SQLite/FTS5 + fallback testuale offline per qmem
Il gateway remoto non è sempre raggiungibile (VPN/nodi giù): finora le ricerche
fallivano e la conoscenza non era consultabile. Ora l'estensione mantiene un
indice locale testuale e vi degrada automaticamente.

Core (extensions/local-db.ts):
- schema SQLite con FTS5 (unicode61 remove_diacritics 2), trigger di sync,
  tabella meta per cursori/stato; usa node:sqlite (Node >= 22.5, nessuna
  dipendenza esterna), con soppressione del warning "experimental"
- import idempotente dalle sessioni pi (tutte le directory di progetto):
  qmem_store/qmem_correct (ID + testo integrale), qmem_get (payload completo),
  qmem_search (record osservati, anche creati da altri agenti)
- merge senza regressioni: le osservazioni povere (es. search senza
  project_id) non azzerano i campi già noti; superseded_by monotono
- ricerca FTS5 con filtri (kind/project/scope/level/topic), esclusione di
  superseduti e privati, ranking bm25, snippet, ripiego AND -> OR dichiarato
- enrich dal gateway (GET /v1/memories/{id}, pacing < rate limit, timeout 8s
  per richiesta, stop al primo guasto) e pull da /v1/memories:export (endpoint
  lato gateway previsto: se assente lo segnala senza errore)
- localGet per il recupero puntuale offline

Estensione:
- qmem_search: su 0/429/5xx degrada all'indice locale, risultati etichettati
  "INDICE LOCALE, ricerca testuale non neurale" + details.fallback=local_sqlite
- qmem_get: fallback locale per UUID
- qmem_store: avviso esplicito che il record NON è salvato (nessuna coda)
- rendering arricchito con project_id e flag privato (anche per il gateway)
- comando /qmem:local status|import|find|enrich|pull
- regole e skill aggiornate: quando si usa l'indice locale non applicare le
  soglie 0.45/0.60 (sono semantiche)

CLI standalone (stesso core): scripts/qmem-sqlite.mjs status|import|find|
enrich|pull (+ --json). Test: scripts/test-local.mjs (14 controlli, HOME
temporanea, sessioni sintetiche, gateway black-hole e stub HTTP).

Verifiche: 14/14 test superati; import reale 185 sessioni -> 1046 record unici
(1032 con testo, 986 attivi, 60 superseduti, 672 con project_id, 20 gruppi di
duplicati) in 2,8 MB; enrich con gateway giù si ferma in ~16s con messaggio
chiaro invece di restare appeso.
2026-09-13 15:53:56 +02:00

124 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# pi-qmem
Memoria centralizzata e condivisa per agenti AI — estensione per pi.
Salva e cerca record semantici (fatti, decisioni, preferenze, episodi) in un
Memory Gateway (FastAPI + Qdrant + BGE-M3) ospitato su un server remoto
raggiungibile via VPN. Nessun LLM in scrittura: l'agente salva record
deliberati e strutturati; il retrieval è vettoriale + filtri metadata.
## Installazione
```bash
pi install git:git.enne2.net/enne2/pi-qmem
```
Oppure copia `extensions/index.ts` in `~/.pi/agent/extensions/pi-qmem/`.
## Tool
| Tool | Descrizione |
|---|---|
| `qmem_store` | Salva un record di memoria (text, kind, agent_id, **project_id obbligatorio**, scope, source, expires_at, supersedes_id, supersede_reason) |
| `qmem_search` | Ricerca semantica su tutta la conoscenza condivisa (query, kind, project_id, scope, top_k, include_superseded, min_score). **Se il gateway non risponde degrada all'indice locale SQLite/FTS5** (testuale, etichettato `fallback: local_sqlite`) |
| `qmem_correct` | Corregge una memoria falsa: crea un nuovo record che **supersede** il vecchio (che resta in archivio marcato superseded) |
| `qmem_meta` | Discovery: panoramica di scope×kind, progetti, agenti e superseduti (per scegliere i filtri di ricerca) |
## Comando
`/qmem:config` — menu interattivo (TUI):
- 🌐 Imposta URL gateway
- 🔑 Cambia API key
- 🔌 Test connessione (verifica URL + validità chiave)
- 📋 Mostra configurazione
- ↩️ Annulla
Modalità CLI rapida: `/qmem:config url <URL> | apikey <KEY> | test`
Config salvata in `~/.config/pi-qmem/config.json` (0600):
```json
{
"url": "https://qmem.enne2.net",
"apiKey": "...",
"localDbPath": "~/.local/share/pi-qmem/qmem.sqlite",
"localFallback": true
}
```
`localFallback: false` disabilita il fallback sull'indice locale (utile per
misurare il comportamento "solo gateway").
## Indice locale (fallback offline)
Il gateway remoto non è sempre raggiungibile (VPN giù, nodi offline). L'estensione
mantiene quindi un **indice locale SQLite + FTS5** che permette di cercare
testualmente la conoscenza **senza gateway, senza modelli, senza dipendenze**:
- **DB**: `~/.local/share/pi-qmem/qmem.sqlite` (override: `localDbPath` in
`~/.config/pi-qmem/config.json` oppure env `QMEM_SQLITE`)
- **Ricostruzione**: dalle sessioni pi (`~/.pi/agent/sessions/<cwd>/*.jsonl`),
incrociando `toolCall``toolResult`: `qmem_store` e `qmem_correct` forniscono
l'**ID del gateway** e il testo integrale, `qmem_get` il payload completo,
`qmem_search` i record visti (anche creati da altri agenti)
- **Ricerca**: FTS5 `unicode61 remove_diacritics 2` (accenti e prefissi),
ranking BM25, filtro dei superseduti/privati di default; se la query in AND non
trova nulla si ripiega su OR (match parziale, dichiarato)
- **Fallback automatico**: `qmem_search`/`qmem_get` usano l'indice locale quando
il gateway risponde 0/429/5xx, etichettando i risultati come **non neurali**
- **Arricchimento**: quando il gateway torna online, `enrich` completa
testo/`project_id`/`private`/stato supersede via `GET /v1/memories/{id}`, e
`pull` sincronizza dall'`export` (endpoint previsto lato gateway)
Comandi (TUI) e CLI standalone:
```bash
/qmem:local status # record, copertura, lag, duplicati
/qmem:local import # ricostruisce/aggiorna dalle sessioni pi
/qmem:local find "circuit breaker" # ricerca testuale locale
/qmem:local enrich [--all] # arricchisce dal gateway
/qmem:local pull # pull incrementale dall'export
# equivalente standalone (stesso core, nessuna dipendenza)
node scripts/qmem-sqlite.mjs status|import|find "query"|enrich|pull
node scripts/test-local.mjs # suite di test (14 controlli, HOME temporanea)
```
Limiti dichiarati: è uno **storico osservato** (più vecchio del gateway), la
ricerca è **lessicale** (nessuno score 0.45/0.60: non applicare le soglie
semantiche) e `qmem_store` **non ha coda locale** — a gateway giù il record non
viene salvato.
## Regole comportamentali (autocontenute)
Le regole vincolanti (obbligo `project_id`, punteggi, correzione/supersede, discovery, **identificazione macchina nei record locali**) sono **distribuite con l'estensione**, senza toccare AGENTS.md:
- **`promptGuidelines`** sui 4 tool (bullets nel `Guidelines` del system prompt, solo quando i tool sono attivi)
- **`before_agent_start`** → blocco "Regole pi-qmem" iniettato nel system prompt a ogni turno (solo se i tool qmem sono attivi); include la sezione **Identificazione macchina** con `MACCHINA CORRENTE` rilevata dinamicamente dall'estensione (`os.hostname()` + `/etc/os-release`)
- **Skill `qmem`** (`skills/qmem/SKILL.md`, standard agentskills.io) → procedura completa on-demand, caricabile con `/skill:qmem`
- Manifest: `pi.skills` nel package.json
## Gateway (componente server)
La cartella `gateway/` contiene il Memory Gateway FastAPI da deployare sul
server (Docker Compose con Qdrant 1.19 + Ollama BGE-M3). Vedi
`gateway/README.md` per il deploy.
## Architettura
```
pi (estensione) ──HTTPS/VPN──▶ Memory Gateway (FastAPI) ──▶ Qdrant 1.19
└──▶ Ollama BGE-M3 (embedding locale)
```
- Accesso condiviso: una chiave API con accesso completo in lettura/scrittura
- `agent_id` è solo metadata di provenienza, non isolamento
- Rate limit, audit log, cleanup automatico dei record scaduti
- **Correzioni**: i record sono immutabili; correggere una memoria falsa = nuovo record che supersede il vecchio (`qmem_correct` o `qmem_store` con `supersedes_id`). Il vecchio resta in archivio con `superseded_by`, escluso dalla ricerca di default (`include_superseded=true` per la lineage)
## Licenza
MIT