← Indice documentazione Microprogettazione › synapse

myclaw

synapse — grafo, fitness, decadimento, potatura
Microprogettazione v1.0 — 22 aprile 2026
Terzo documento dell'estensione «Neuroni e Memoria».
Reifica Neuroni+Memoria v1.1 §4 (fitness) e §5 (sinapsi).

Pubblico: chi implementerà il grafo, il retriever darwiniano, il job di potatura. Lettura: 20 min.

Indice

  1. Scopo e confini
  2. Il grafo: nodi e archi
  3. Fitness di un neurone
  4. Peso di una sinapsi
  5. Decadimento: Ebbinghaus + ACT-R
  6. Retriever: dal grafo al candidato
  7. Potatura e quarantena
  8. Contratto Python
  9. Implementazione di default
  10. Alternative considerate
  11. Test di conformità
  12. Riferimenti

1. Scopo e confini

Una volta nati, i neuroni vivono in un grafo emergente: i nodi sono i neuroni, gli archi sono le co-attivazioni reali. Questo documento definisce come il grafo viene costruito, aggiornato, letto, e come alimenta la legge darwiniana (fitness) che regola chi resta e chi viene potato.

Cosa copre

Cosa non copre

2. Il grafo: nodi e archi

Il grafo è costruito per aggregazione dai journal.sqlite di ogni neurone (vedi neuron §6) e materializzato in un DB SQLite centrale workspace/neurons/_graph.sqlite aggiornato da un job periodico.

-- workspace/neurons/_graph.sqlite

CREATE TABLE nodes (
    name             TEXT PRIMARY KEY,
    version          TEXT NOT NULL,
    state            TEXT NOT NULL,        -- neonato|attivo|dormiente|quarantena|estinto
    fitness          REAL DEFAULT 0.0,     -- aggregato; vedi §3
    invocations      INTEGER DEFAULT 0,
    last_invoked_at  DATETIME,
    last_success_at  DATETIME,
    strikes          INTEGER DEFAULT 0,    -- error streak recente
    updated_at       DATETIME NOT NULL
);

CREATE TABLE edges (
    src              TEXT NOT NULL,
    dst              TEXT NOT NULL,
    declared         BOOLEAN NOT NULL,     -- da manifest.may_call
    co_activations   INTEGER DEFAULT 0,    -- observed
    last_co_at       DATETIME,
    weight           REAL DEFAULT 0.0,     -- vedi §4
    PRIMARY KEY (src, dst)
);

CREATE INDEX idx_edges_src ON edges(src);
CREATE INDEX idx_edges_weight ON edges(weight DESC);

Un arco può essere:

3. Fitness di un neurone

La fitness misura riduzione del gap nel tempo. Non "quante volte è stato invocato" ma "quanto ha avvicinato myclaw al suo scopo". È la quantità che decide visibilità nel retriever e, sotto soglia, potatura.

Utilità di una singola invocazione

u(n, g) = Gappre(g) − Gappost(g)

Se u > 0: il neurone ha avvicinato all'obiettivo.
Se u = 0: neutro (risposta vuota o inutile ma non dannosa).
Se u < 0: il neurone ha peggiorato la situazione (errore, side-effect indesiderato).

Il Gap viene calcolato dal caller (reasoning loop) secondo la metrica associata al tipo di task:

Tipo di taskMetrica GapAffidabilità
ConcretoPassi residui per raggiungere lo stato, exit code, tempo wall.Alta (numerica).
SfumatoPunteggio critic-LLM + feedback utente esplicito; asimmetria prudenziale (negativo pesa 2×).Media.
SconosciutoGap non misurabile → invocazione marcata u=null.Zero.

Aggregazione nel tempo

fitness(n) = sum over invocations i of:
    u(i) * decay_factor(age_days(i))
  + exploration_bonus(n)

decay_factor(d) = exp(-d / TAU)        # TAU = 30 giorni (Ebbinghaus-like)

exploration_bonus(n) =
    B0 if state == "neonato" else 0    # B0 = 0.5, decade a 0 appena attivo

Le invocazioni con u=null non contribuiscono alla fitness ma contano nel contatore invocations (per far uscire i neuroni dalla fase "sconosciuto").

DECISIONE v1: TAU = 30 giorni. Scelta pragmatica: decadimento abbastanza lento da tollerare uso sporadico, abbastanza rapido da far invecchiare utilità vecchie. Tunabile in config.

4. Peso di una sinapsi

Il peso di un arco (src, dst) cresce con la co-attivazione e decade con il silenzio:

weight(src, dst) =
    log(1 + co_activations)         # saturazione sublineare: evita dominanza
  * decay_factor(days_since_last_co)
  * success_ratio(src -> dst)       # frazione di co-attivazioni in trace con outcome=success

5. Decadimento: Ebbinghaus + ACT-R

decadimento base (TAU=30gg) co-attivazione → boost la curva di decadimento è tipica Ebbinghaus: steep early, flat late giorni dall'ultima co-attivazione peso soglia potatura
Figura 1 — Peso della sinapsi nel tempo. Senza rinforzo, decade esponenzialmente. Ogni co-attivazione (pallino verde) lo fa risalire: è il rinforzo hebbiano. Sotto la soglia di potatura per 60 giorni, l'arco viene rimosso.

La scelta del doppio riferimento (Ebbinghaus per la forma, ACT-R per il rinforzo via access-count) è intenzionale:

6. Retriever: dal grafo al candidato

Dato uno scopo g corrente, il retriever ritorna un piccolo insieme di neuroni candidati ordinati. È il punto in cui il grafo parla al reasoning loop.

Algoritmo (pseudocodice)

def retrieve(goal, top_k=5):
    # 1. Candidati per similarità semantica
    cands = vector_search(embed(goal), table="nodes_purpose", top=20)

    # 2. Filtri duri
    cands = [n for n in cands
             if n.state in {"neonato", "attivo"}
             and policy.allows(n, current_ctx)]

    # 3. Scoring
    scored = []
    for n in cands:
        s = w1 * similarity(n, goal)          # relevance
          + w2 * normalize(n.fitness)         # fitness
          + w3 * neighborhood_bonus(n, goal)  # amici attivi
        scored.append((s, n))

    # 4. Bandit: quota esplorativa
    if random() < EPSILON:
        pick = ucb_choice(cands, exclude=best(scored))
        return [pick] + sorted(scored, reverse=True)[:top_k-1]
    else:
        return sorted(scored, reverse=True)[:top_k]

Default dei pesi

PesoRuoloDefault
w1 (relevance)Similarità semantica purpose ↔ goal0.50
w2 (fitness)Successi passati0.30
w3 (neighborhood)Il neurone è connesso a neuroni già utili per goal simili0.20
EPSILONFrazione esplorativa (bandit)0.05
Perché una quota esplorativa. Senza EPSILON, i neuroni ad alta fitness iniziale occupano tutti gli slot per sempre (rich-get-richer). Un neurone rarissimo ma indispensabile in situazioni di nicchia non troverebbe mai spazio. Il bandit assicura che, ogni tanto, un candidato sub-ottimale venga provato.

7. Potatura e quarantena

Job notturno (default 03:30)

  1. Aggrega: unisce i journal.sqlite dei neuroni in _graph.sqlite.
  2. Ricalcola fitness e weight per tutti i nodi/archi.
  3. Transizioni di stato:
  4. Potatura archi: archi con weight < 0.05 da > 60 gg vengono rimossi da edges.
  5. Digest per Roberto: riassunto di transizioni non banali (estinzioni, promozioni, cambi forti di fitness), approvazione non richiesta ma visibile.
L'estinzione non è cancellazione. La directory del neurone viene spostata in .audit/neurons_extinct/YYYY-MM/ in sola lettura. Forensica, reversibilità, e conformità alla Legge 3 (tracciabilità) esigono che niente sparisca davvero.

8. Contratto Python

from typing import Protocol
from dataclasses import dataclass
from datetime import datetime

@dataclass
class GraphNode:
    name: str
    state: str
    fitness: float
    invocations: int
    last_invoked_at: datetime | None
    strikes: int

@dataclass
class GraphEdge:
    src: str
    dst: str
    declared: bool
    co_activations: int
    weight: float
    last_co_at: datetime | None

@dataclass
class RetrievalResult:
    candidates: list[GraphNode]
    picked_by: Literal["score", "exploration"]

class SynapseGraph(Protocol):
    async def rebuild(self) -> None:
        """Job notturno: riaggrega journal, ricalcola fitness e weight,
        applica transizioni di stato, potatura archi."""
        ...

    async def observe_invocation(
        self,
        neuron: str,
        trace_id: str,
        gap_pre: float | None,
        gap_post: float | None,
        outcome: str,
    ) -> None:
        """Hot-path: registra un'invocazione, aggiorna strikes e last_invoked_at.
        Il ricalcolo pieno avviene nel job notturno."""
        ...

    async def observe_co_activation(
        self, src: str, dst: str, trace_id: str, success: bool,
    ) -> None: ...

    async def retrieve(
        self, goal_text: str, context: dict, top_k: int = 5,
    ) -> RetrievalResult: ...

    async def transition(self, name: str, new_state: str, reason: str) -> None: ...

# Errori
class NeuronNotInGraphError(Exception): ...
class PolicyRetrievalVetoError(Exception): ...

9. Implementazione di default

AspettoScelta v1Motivazione
Storage grafoSQLite _graph.sqliteStessa famiglia dei journal; join semplici per il retriever.
Vector searchEmbedding su purpose + cosine in SQLite (BLOB + funzione custom)Scala fino a migliaia di neuroni; se superiamo, passiamo a FAISS locale.
Aggiornamento fitnessIncrementale su hot-path (strikes, last_invoked_at) + batch notturno per il calcolo pienoBilancia freschezza e costo CPU.
Banditε-greedy con ε fisso 0.05Semplice, robusto. UCB-like più sofisticato rimandato a v2 se serve.
Scheduler jobCron interno del gatewayEvita dipendenza da cron di sistema.

10. Alternative considerate

AlternativaPerché scartata (o rimandata)
Database a grafo dedicato (Neo4j)Overhead infrastrutturale; query tipiche (top-K, scan per stato) sono felici con SQLite.
Fitness calcolata solo on-demandCosto per ogni retrieve sale linearmente con la storia. Meglio aggregato notturno + delta incrementale.
Decay lineare (no Ebbinghaus)Non riflette il comportamento osservato dell'uso sporadico-ma-utile; la curva esponenziale è più fedele.
Niente bandit (puro greedy)Rich-get-richer porta a una library che si fossilizza attorno ai primi neuroni buoni.
UCB1 / Thompson samplingComplesso da spiegare e tunare; ε-greedy basta per il volume di uso previsto. Pronti a passare se il retriever diventa il collo di bottiglia.
Cancellazione dura (rm -rf) all'estinzioneViola la Legge 3 (tracciabilità). Tutto va in .audit/.
HippoRAG personalized PageRankIn valutazione. Utile quando il grafo è grande (> 500 nodi). Oggi è over-engineering.

11. Test di conformità

InvarianteTest
Invocazione aggiorna hot-pathobserve_invocation(...)nodes.last_invoked_at aggiornato entro 100ms, invocations++.
Strike si incrementa su errorOutcome=error → strikes+=1; outcome=success → strikes=0.
3 strike → quarantena3 observe_invocation consecutive con outcome=error → state = quarantena dopo prossimo rebuild.
Fitness decade senza usoNeurone senza invocazioni per 30 gg: fitness(t+30) < fitness(t) · 0.4 (per TAU=30).
Arco dichiarato-e-osservatoDopo co_activation valida: edges row con declared=1, co_activations++, weight > 0.
Arco osservato fuori may_call bloccatoTentativo di co_activation su coppia non dichiarata → UndeclaredSynapseError, nessun edge creato.
Potatura archi deboliEdge con weight < 0.05 per > 60 gg → dopo rebuild non presente in edges.
Retriever filtra statoNeurone in quarantena NON appare mai in RetrievalResult.candidates.
Retriever rispetta policy vetoNeurone con capability ritirata → escluso dai candidati, non solo de-rankato.
Bandit ha quota esplorativaSu 10000 retrieve con la stessa query, ≥ 3% dei picked_by è "exploration".
Estinzione preserva fileTransizione quarantena → estinto: directory spostata in .audit/neurons_extinct/YYYY-MM/, file presenti, sola lettura.
Rebuild idempotenteDue rebuild consecutivi senza nuove invocazioni producono lo stesso stato (fitness, edges, weights).

12. Riferimenti

RiferimentoCosa abbiamo preso
Neuroni+Memoria v1.1 §4Definizione di Gap, utilità, fitness; caveat sulla self-valutazione.
Neuroni+Memoria v1.1 §5Sinapsi dichiarate/osservate, decadimento, potatura.
Ebbinghaus (1885)Curva di oblio esponenziale come forma canonica del decay.
ACT-R activation (Anderson)Rinforzo via access-count; log(1 + uses) come saturazione.
MemoryBank (Zhong et al. 2023)Integrazione Ebbinghaus + rinforzo già applicata a LLM memory.
Generative Agents (Park 2023)Retrieval come relevance × recency × importance.
Sutton & Barto (bandit)ε-greedy come baseline robusto per selezione-vs-esplorazione.
Huang et al. 2023"LLMs cannot self-correct": preferire metriche oggettive alla self-valutazione.
Neuron §6Fonte: journal per-neurone che alimenta il grafo.

Continua a leggere

prossimo
constitution
Le 4 Leggi, SOUL.md, il rito di modifica. Le regole che vincono sui numeri.
microprogettazione
neuron
Il journal per-neurone che alimenta il grafo.
microprogettazione
memory
Vocabolario CoALA, formule di retrieval gemelle.
indice
Torna alla landing
Microprogettazione, tutti i doc.

myclaw — synapse microprogettazione v1.0 — 2026-04-22
Terzo doc dell'estensione neuroni. Prossimo: constitution.html.