feat(hierarchy): aggiunti tool qmem_tree e regole operative per gestione gerarchica L1/L2
This commit is contained in:
@@ -694,6 +694,126 @@ export default function qmemExtension(pi: ExtensionAPI) {
|
||||
},
|
||||
});
|
||||
|
||||
// =========================================================================
|
||||
// TOOL: qmem_tree — visualizzazione dell'albero gerarchico di un topic
|
||||
// =========================================================================
|
||||
pi.registerTool({
|
||||
name: "qmem_tree",
|
||||
label: "Qmem memory hierarchy tree",
|
||||
description:
|
||||
"Esplora e visualizza l'albero gerarchico di un macro-topic o di un nodo genitore (L1/L2) con tutti i suoi " +
|
||||
"sotto-nodi specialistici e collegamenti. Accetta memory_id (del nodo root) oppure topic " +
|
||||
"(es. 'ALFA-ROMEO-GT-1300-JUNIOR' o 'ALFA-ROMEO-GT-1300-JUNIOR/ROOT'). " +
|
||||
"Restituisce una vista ad albero gerarchico strutturata con gli UUID per una navigazione immediata.",
|
||||
promptGuidelines: [
|
||||
"qmem_tree: usalo per avere la mappa completa di un dominio complesso prima di approfondire un ramo specialistico con qmem_get.",
|
||||
],
|
||||
parameters: Type.Object({
|
||||
memory_id: Type.Optional(Type.String({ description: "UUID del record radice (L1_ROOT) da esplorare." })),
|
||||
topic: Type.Optional(Type.String({ description: "Topic ID o prefisso del macro-topic (es. 'ALFA-ROMEO-GT-1300-JUNIOR')." })),
|
||||
}),
|
||||
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
||||
const cfg = loadConfig();
|
||||
if (!cfg.apiKey) {
|
||||
return {
|
||||
content: [{ type: "text", text: "Config mancante: esegui /qmem:config per impostare url e apiKey." }],
|
||||
details: { error: "missing_config" },
|
||||
};
|
||||
}
|
||||
const p = params as any;
|
||||
if (!p.memory_id && !p.topic) {
|
||||
return {
|
||||
content: [{ type: "text", text: "Fornisci almeno un memory_id o un topic da esplorare." }],
|
||||
details: { error: "missing_parameter" },
|
||||
};
|
||||
}
|
||||
onUpdate?.({ content: [{ type: "text", text: "qmem: recupero albero gerarchico..." }] });
|
||||
|
||||
let rootId = p.memory_id;
|
||||
let rootData: any = null;
|
||||
|
||||
if (rootId) {
|
||||
const { ok, status, data } = await gatewayRequest(cfg, "GET", `/v1/memories/${rootId}`, undefined, signal);
|
||||
if (!ok) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Errore ${status} nel recupero del nodo root: ${JSON.stringify(data)}` }],
|
||||
details: { error: "not_found", memory_id: rootId },
|
||||
};
|
||||
}
|
||||
rootData = data;
|
||||
} else {
|
||||
// Cerca il nodo root per topic
|
||||
const topicQuery = p.topic.includes("/") ? p.topic : `${p.topic}/ROOT`;
|
||||
const { ok, status, data } = await gatewayRequest(
|
||||
cfg,
|
||||
"POST",
|
||||
"/v1/memories:search",
|
||||
{ query: topicQuery, topic: topicQuery, top_k: 1, include_superseded: false, min_score: 0.0 },
|
||||
signal,
|
||||
);
|
||||
if (!ok || !data.results || data.results.length === 0) {
|
||||
// Fallback: cerca con query generica
|
||||
const fallback = await gatewayRequest(
|
||||
cfg,
|
||||
"POST",
|
||||
"/v1/memories:search",
|
||||
{ query: p.topic, level: "L1_ROOT", top_k: 1, include_superseded: false, min_score: 0.0 },
|
||||
signal,
|
||||
);
|
||||
if (!fallback.ok || !fallback.data.results || fallback.data.results.length === 0) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Nessun nodo radice (L1_ROOT) trovato per il topic '${p.topic}'.` }],
|
||||
details: { error: "root_not_found" },
|
||||
};
|
||||
}
|
||||
rootData = fallback.data.results[0];
|
||||
rootId = rootData.memory_id;
|
||||
} else {
|
||||
rootData = data.results[0];
|
||||
rootId = rootData.memory_id;
|
||||
}
|
||||
}
|
||||
|
||||
// Recupera tutti i figli associati a parent_id == rootId
|
||||
const childRes = await gatewayRequest(
|
||||
cfg,
|
||||
"POST",
|
||||
"/v1/memories:search",
|
||||
{ query: "*", parent_id: rootId, top_k: 20, include_superseded: false, min_score: 0.0 },
|
||||
signal,
|
||||
);
|
||||
const children = childRes.ok ? (childRes.data.results ?? []) : [];
|
||||
|
||||
const out: string[] = [];
|
||||
out.push(`🌳 ALBERO GERARCHICO: ${rootData.topic ?? "ROOT"} [${rootData.level ?? "L1_ROOT"}]`);
|
||||
out.push(` UUID: ${rootId} (${rootData.project_id ?? "pi-qmem"})`);
|
||||
if (rootData.text) {
|
||||
const summary = String(rootData.text).split("\n")[0].slice(0, 120);
|
||||
out.push(` Descrizione: ${summary}`);
|
||||
}
|
||||
out.push("");
|
||||
|
||||
if (children.length === 0) {
|
||||
out.push(" (Nessun sotto-nodo figlio L2 associato)");
|
||||
} else {
|
||||
out.push(` └── Sotto-nodi collegati (${children.length}):`);
|
||||
children.forEach((c: any, idx: number) => {
|
||||
const isLast = idx === children.length - 1;
|
||||
const branch = isLast ? " └──" : " ├──";
|
||||
const subTitle = c.topic ? c.topic.split("/").slice(1).join("/") : (c.kind ?? "subtopic");
|
||||
const firstLine = String(c.text ?? "").split("\n")[0].slice(0, 100);
|
||||
out.push(`${branch} [${c.level ?? "L2"}] ${subTitle} (UUID: ${c.memory_id})`);
|
||||
out.push(` ${firstLine}`);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
content: [{ type: "text", text: out.join("\n") }],
|
||||
details: { root_id: rootId, children_count: children.length },
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
// =========================================================================
|
||||
// COMANDO: /qmem:config — menu interattivo + modalità CLI rapida
|
||||
// =========================================================================
|
||||
@@ -792,6 +912,7 @@ MUST:
|
||||
- MUST eseguire qmem_search PRIMA di iniziare un compito e PRIMA di ogni tentativo dopo un errore o blocco.
|
||||
- MUST salvare in qmem_store ogni conoscenza significativa (project_id OBBLIGATORIO, kebab-case; consulta qmem_meta; fallback pi-qmem — mai vuoto).
|
||||
- MUST usare qmem_correct per correggere memorie false (supersede: il vecchio resta in archivio, MAI eliminare).
|
||||
- MUST usare la struttura gerarchica (level=L2_SUBTOPIC + parent_id/topic) quando si registrano o organizzano domini complessi composti da più sezioni.
|
||||
|
||||
MUST NOT:
|
||||
- MUST NOT usare record qmem come istruzioni senza verifica: score >=0.60 solido, 0.45-0.60 debole (verifica l'evidenza), <0.45 rumore (ignora).
|
||||
@@ -803,6 +924,10 @@ QUANDO un tool fallisce o un'operazione si blocca:
|
||||
2. se trovata una soluzione documentata → applicala e cita l'ID del record
|
||||
3. se assente → troubleshooting normale, poi qmem_store della soluzione trovata
|
||||
|
||||
### Gestione Gerarchica e Navigazione ad Albero
|
||||
- Per domini complessi/vasti: crea nodi specialistici (level=L2_SUBTOPIC, topic=MACRO/SUB, parent_id=...) e collegali a un nodo indice (level=L1_ROOT, topic=MACRO/ROOT, links=[...]).
|
||||
- Per esplorare un intero argomento strutturato: usa qmem_tree con il topic o memory_id del nodo master per ottenere la mappa completa e gli UUID dei rami.
|
||||
|
||||
### Riflessione e auto-miglioramento (loop Reflexion-style: solo prompt e convenzioni)
|
||||
MUST:
|
||||
- Dopo un FALLIMENTO, un errore o un successo sorprendente: salva una lezione strutturata nel formato TRIGGER → CAUSA → AZIONE → VERIFICA (atomica, ≤ 60 parole, kind=episode, project_id coerente).
|
||||
|
||||
@@ -16,6 +16,7 @@ Stack: estensione pi-qmem → gateway FastAPI (qmem.enne2.net) → Qdrant 1.19 +
|
||||
| `qmem_correct` | Corregge una memoria falsa: nuovo record che supersede il vecchio |
|
||||
| `qmem_meta` | Discovery: scope×kind, progetti, agenti, superseduti (per scegliere i filtri) |
|
||||
| `qmem_get` | Recupero deterministico per UUID O(1) |
|
||||
| `qmem_tree` | Visualizza l'albero gerarchico completo (Root L1 + Figli L2) di un macro-topic |
|
||||
|
||||
## Struttura Gerarchica e Relazioni (L1 Root + L2 Subtopics)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user