← Indice documentazione Microprogettazione › synthesizer

myclaw

synthesizer — pipeline di nascita di un neurone
Microprogettazione v1.0 — 22 aprile 2026
Secondo documento dell'estensione «Neuroni e Memoria».
Reifica Neuroni+Memoria v1.1 §3 (il ciclo di sintesi).

Pubblico: chi implementerà il loop di generazione e i gate di sicurezza. Lettura: 18 min.

Indice

  1. Scopo e confini
  2. I sette stadi
  3. Trigger: quando parte la sintesi
  4. Stadio 2 — Spec
  5. Stadio 3 — Bozza
  6. Stadio 4 — Analisi statica
  7. Stadio 5 — Test in sandbox
  8. Stadio 6 — Approvazione umana
  9. Stadio 7 — Firma e attivazione
  10. Retry e abbandono
  11. Contratto Python
  12. Alternative considerate
  13. Test di conformità
  14. Riferimenti

1. Scopo e confini

Il synthesizer è il componente che trasforma un fallimento motivato in un neurone candidato. È una pipeline a 7 stadi con retry massimo 3 e un gate umano non bypassabile allo stadio 6. Quello che produce è una directory-neurone pronta per essere firmata.

Cosa copre

Cosa non copre

2. I sette stadi

1. Trigger tool esauriti gap rimane runtime.decide() 2. Spec cosa deve fare I/O schema capability 3. Bozza LLM frontier body.py + test + manifest.yaml 4. Statica ruff + bandit AST whitelist cap coerenza 5. Sandbox test_birth.py bwrap tight timeout 30s 6. Utente diff + esiti capability gate umano 7. Firma & attivazione HMAC → signature.hmac loader ricarica il neurone stato: neonato rifiuto → spec rivista (max 3 iter., poi abbandono)
Figura 1 — La pipeline in sette stadi. Stadi 4, 5, 6 sono gate: un rifiuto rimanda a bozza (o a spec, se l'utente lo chiede). Dopo tre cicli falliti il synthesizer abbandona, comunica all'utente, logga un episode di fallimento per reflection.
#StadioOutputCosto tipico
1TriggerSynthesisRequest (scopo, trace originale, capability desiderate)0 (decisione logica)
2SpecNeuronSpec (purpose, I/O schema, cap)1 chiamata LLM frontier, ~0.5€
3Bozzabody.py + test_birth.py + manifest.yaml (draft)1–2 chiamate LLM frontier, ~0.8€
4StaticaReport lint + verdetto ok/fail~200 ms locale
5Sandbox + reward RReport test + metriche + R ∈ [0,1] con breakdown (§7.1)~1–30 s sandbox + ~0.05€ judge local-fast
6UtenteDecisione informata da R: approva / rivedi / scartaumano (asincrono, ma molto più veloce grazie al breakdown)
7Firma & attivazione + bootstrap TrustScoresignature.hmac scritto, neurone caricato, TrustScore iniziale = R~50 ms locale

3. Trigger: quando parte la sintesi

Il synthesizer non decide da solo: il agent_runtime emette una SynthesisRequest quando almeno uno dei seguenti è vero, e nessun veto della policy è attivo:

Trigger esterno (reattivo)

Trigger interno (proattivo, presidiato)

DECISIONE DA CONFERMARE. In fase 1 solo il trigger esterno è attivo. Quello interno richiede il grafo di fitness e un aggregato storico; si attiva in fase 6 quando synapse è implementato.

4. Stadio 2 — Spec

Una chiamata LLM frontier (purpose design-neuron-spec) produce uno NeuronSpec strutturato. Il prompt include: la trace fallita, i tool già tentati, il catalogo delle capability disponibili, lo stile degli spec precedenti (few-shot).

@dataclass(frozen=True)
class NeuronSpec:
    proposed_name: str
    purpose: str                   # 1-4 frasi NL
    input_schema: dict             # JSON Schema
    output_schema: dict
    capabilities_requested: list[str]
    rationale: str                 # perché serve, cosa non bastava
    may_call: list[str]            # tool/neuroni attesi

La spec passa un pre-check di policy: se richiede capability forbidden (es. shell:* fuori dalla whitelist), viene rigettata subito senza consumare la quota di retry.

5. Stadio 3 — Bozza

Seconda chiamata LLM frontier (purpose write-neuron-code). Input: lo NeuronSpec, il template skeleton del corpo, l'API di NeuronContext, esempi di neuroni esistenti ad alta fitness (few-shot). Output: tre file (body.py, test_birth.py, manifest.yaml).

Vincoli imposti nel prompt

6. Stadio 4 — Analisi statica

Lanciata in locale, senza sandbox (sta analizzando testo, non eseguendo). Tre check in cascata:

  1. Lint: ruff check su body.py e test_birth.py. Errore = rigetto.
  2. Security lint: bandit con profilo strict.
  3. AST whitelist: parsing AST, verifica che solo gli import ammessi siano presenti e che non ci siano chiamate a eval, exec, compile, __import__, open (deve passare da ctx.open).
  4. Coerenza capability: le capability in manifest.yaml devono coprire tutte le chiamate che il body effettivamente fa (es. ctx.open("~/logs", ...) richiede fs-read:~/logs).
DECISIONE v1: la whitelist AST parte molto stretta. Se in pratica taglia troppi neuroni legittimi, estenderla con revisione umana un simbolo alla volta. Restringere è difficile, allargare è facile.

7. Stadio 5 — Test in sandbox

test_birth.py viene eseguito dentro un profilo bwrap tight (vedi sandbox §3):

Output: SandboxReport con esito per ciascun test, wall-clock, ram peak, stdout/stderr troncati.

7.1 Synthetic reward composito R

Dopo il SandboxReport lo stadio 5 non produce un verdetto binario ma uno score continuo R ∈ [0, 1]. La formula è canonizzata in rl_offline §5 (Esperimento B):

R = 0.40 · det_pass_rate       # frazione test deterministici verdi
  + 0.25 · judge_score         # LLM-as-judge (local-fast) su rubrica costituzionale
  + 0.15 · cost_ratio          # clip(1 − cost_sandbox_effettivo / stima, 0, 1)
  − 0.10 · similarity_penalty  # cosine-sim embedding con neuroni esistenti > 0.85
  + 0.10 · coverage_bonus      # bonus se copre effect_class non coperto

gate_threshold = 0.65          # DECISIONE v1
EsitoAzione
R ≥ 0.65 Pass: lo stadio 5 passa al 6 (approvazione umana) con dossier che include il breakdown R.
R < 0.65 Rigetto soft: il breakdown è iniettato in-context nella prossima iterazione di bozza ("det_pass_rate=0.40, judge segnala law.1 borderline, cost_ratio basso: riprova tenendo conto"). Consuma 1 retry dei max 3.
Test fallisce (hard) Se det_pass_rate = 0 o viene violato un limite sandbox (OOM, timeout > 30s, exit ≠ 0 su tutti i test): rigetto hard, ritorno a bozza senza calcolare R. Consuma 1 retry.
Il giudice è un modello economico. judge_score è calcolato invocando il tier local-fast (vedi policy §6 cost_tiering) con una rubrica fissa che include la Costituzione renderizzata. Non è un reward model appreso: è un prompt con criteri espliciti, ispezionabile e modificabile come qualunque altro prompt di sistema. Max 500 token di output, formato {"score": float, "reasoning": str}.
DECISIONE v1 (soglia 0.65): calibrata per filtrare i casi mediocri senza essere restrittiva sui buoni. Per scenario: La soglia si sposta con il tempo d'uso: rivalutare dopo 20 sintesi reali.

8. Stadio 6 — Approvazione umana

Questo stadio è non bypassabile in ogni livello di autonomy. È la valvola di non-ritorno prescritta dalla Legge 2: un agente non si auto-modifica senza consenso informato.

Cosa vede Roberto

Effetto sul carico di approvazione. Il dossier informato dal reward R è la differenza fra "Roberto deve leggere 200 righe di codice per decidere" e "Roberto vede uno score 0.85 col breakdown e il giudizio testuale, e decide in 20 secondi". Questa è la riduzione di carico dei 4 driver del progetto.

Azioni possibili

AzioneEffetto
ApprovaVai allo stadio 7 (firma & attivazione).
Approva con riservaNeurone attivo ma solo su scopo corrente, retrieval disabilitato. Promozione a retrieval normale richiede un secondo OK dopo 5 invocazioni pulite.
Rivedi specRimanda a stadio 2 con feedback NL dell'utente. Conta come 1 retry.
Rivedi bozzaRimanda a stadio 3 con feedback. Conta come 1 retry.
ScartaSynthesizer abbandona. Registra un episode fallimento; in modalità interna il pattern alla base viene marcato "direzione rifiutata" per 30 giorni.

La UX concreta (CLI, Telegram, batching) è in approval_ux.

9. Stadio 7 — Firma e attivazione

  1. Normalizzazione dei tre file: rimozione trailing whitespace, line endings LF.
  2. Calcolo HMAC-SHA256 secondo il contratto di neuron §5.
  3. Scrittura di signature.hmac.
  4. Bootstrap TrustScore: scrittura in TrustStore (rl_offline §7) di un record iniziale per il subject ("neuron:<name>", "invoke") con score = R (dallo stadio 5), n_samples = 1, componente human_approval_rate = 1.0 (è appena stato approvato). Il neurone parte con fiducia parziale proporzionale alla qualità della nascita.
  5. Notifica al loader tramite il watcher: viene ricaricato, stato iniziale neonato.
  6. Audit event neuron.born con nome, versione, trace_id originale, hash del body, R finale.
Conseguenza operativa. Un neurone nato con R=0.85 è pronto a essere usato dal runtime ma richiede ancora approvazione umana per side-effect (autonomy standard). Dopo ~10 invocazioni pulite, il TrustScore accumulato dall'Esperimento A lo porterà sopra la soglia di promozione, e la policy inizierà a far passare le sue azioni green-zone senza chiedere a Roberto.

10. Retry e abbandono

Niente retry silenzioso oltre la terza iterazione. Un agente che continua a generare neuroni finché uno "passa" è un agente che degrada la qualità della library. Tre è il numero (arbitrario, tunabile) che dà respiro senza diventare una lotteria.

11. Contratto Python

from typing import Protocol, Literal
from dataclasses import dataclass

SynthesisOutcome = Literal["born", "abandoned", "rejected_direction"]

@dataclass
class SynthesisRequest:
    goal: str                       # scopo in NL
    trace_id: str                   # trace originale che ha fallito
    mode: Literal["external", "internal"]
    capability_hint: list[str]      # ipotesi iniziali
    budget_cents: float             # tetto frontier spendibile

@dataclass
class RewardBreakdown:
    """Score composito R dello stadio 5 (vedi §7.1). Reso visibile a Roberto
    nel dossier di stadio 6 e al sintetizzatore come feedback in-context
    quando R < gate. Formula canonica in rl_offline §5."""
    det_pass_rate: float       # [0,1]
    judge_score: float         # [0,1] dal tier local-fast
    judge_reasoning: str       # 2-3 righe di motivazione
    cost_ratio: float          # [0,1]
    similarity_penalty: float  # [0,1] (con segno negativo nella formula)
    coverage_bonus: float      # [0,1]
    total: float               # R aggregato

@dataclass
class SynthesisResult:
    outcome: SynthesisOutcome
    neuron_name: str | None
    neuron_path: str | None
    retries_used: int
    cost_cents: float
    rejection_reason: str | None
    final_reward: RewardBreakdown | None = None   # popolato se arriva a stadio 5

class Synthesizer(Protocol):
    async def synthesize(self, req: SynthesisRequest) -> SynthesisResult:
        """Esegue la pipeline a 7 stadi. Non solleva: ritorna sempre un
        SynthesisResult, anche su abbandono."""
        ...

    async def revise(
        self, request_id: str, feedback: str,
        target_stage: Literal["spec", "draft"],
    ) -> SynthesisResult:
        """Riprende una request in pausa dopo rifiuto utente allo stadio 6."""
        ...

# Errori interni (non propagati; registrati e trasformati in outcome)
class StaticAnalysisError(Exception): ...
class SandboxTestError(Exception): ...
class PolicyVetoError(Exception): ...
class BudgetExceededError(Exception): ...

12. Alternative considerate

AlternativaPerché scartata (o rimandata)
Nessuna analisi statica, solo sandboxLa sandbox non cattura classi di problemi (import disallowed, dead code, capability drift). Statica prima filtra l'80%.
Un solo LLM-call per spec+bozzaPrompt enorme, qualità minore, diff difficili da ispezionare. Separare dà due punti di verifica.
Approvazione opzionale per neuroni "non-I/O"Anche un neurone "puro" può ciclare a vuoto, consumare quota, inquinare la library. Gate umano sempre.
Retry illimitato con prompting adattivoDegrada il budget e la qualità media della library. Max 3 è tuning conservativo.
Sintesi senza test_birthIl gate sandbox diventa inutile (nulla da eseguire). Il test è la parte non negoziabile.
Generazione da più LLM in parallelo e selezione del miglioreCosto 3×, per uso domestico non giustificato. Eventuale in v2 se la pipeline ha hit-rate basso.

13. Test di conformità

InvarianteTest
Trigger rispetta il veto policyRequest con capability forbidden → outcome abandoned, retries_used = 0, nessun LLM-call emesso.
Static fallisce → ritorno a bozzaIniettare os.system(...) in body draft → ruff/bandit lo rifiutano, stadio 3 ri-partito con feedback.
Whitelist AST enforcedImport di socket nella bozza → rigetto con error_class ForbiddenImport.
Timeout sandbox → rigettotest_birth con while True → SandboxReport timeout=true, outcome stadio 5 fail.
Capability coerenzabody chiama ctx.open("/etc/passwd") ma manifest non dichiara la cap → coerenza fallisce, rigetto.
Gate umano sempre presenteNon esiste path di codice che approvi un neurone senza passare dal callback approval_ux. Test: mock approval_ux → senza mock chiamato, nessun signature.hmac scritto.
Max 3 retryForzare 3 rigetti consecutivi → outcome = abandoned, retries_used = 3.
Abbandono logga episodeOutcome abandoned → un record in episodes di memory con outcome synthesis_failed.
Budget enforcedRequest con budget 0.10€ → BudgetExceededError interno → outcome abandoned, costo effettivo ≤ 0.10€.
Internal mode doppio gateRequest mode=internal → due approvazioni distinte richieste prima della firma.
Pattern rifiutato: lock 30 ggRifiuto direzione interna su pattern P → nessun nuovo synthesize interno per P per 30 gg.
Signature scritta dopo approvazionesignature.hmac esiste ⇔ stadio 7 concluso con outcome=born.

14. Riferimenti

RiferimentoCosa abbiamo preso
Neuroni+Memoria v1.1 §3I sette stadi, il ciclo di vita, l'approvazione obbligatoria.
Neuroni+Memoria v1.1 §4Modalità esterna vs interna, doppio gate.
Voyager (Wang et al. 2023)Iterative prompting + test-driven skill generation.
Self-Debugging (Chen et al. 2023)Ciclo "esegui, leggi errore, correggi". Ispira il feedback loop fra stadi 4–5 e stadio 3.
Gorilla / ToolBenchSchema-first generation: prima il contratto, poi il codice.
Bandit / ruffGate statici concreti.
Sandbox §3Profilo bwrap tight usato in stadio 5.
Approval UXIl come presentare all'utente la scelta allo stadio 6.

Continua a leggere

prossimo
synapse
Il grafo dei neuroni dopo la nascita: fitness, decadimento, potatura.
microprogettazione
neuron
Anatomia del neurone che il synthesizer produce.
microprogettazione
approval_ux
Il "come" lo stadio 6 è presentato all'utente.
indice
Torna alla landing
Microprogettazione, tutti i doc.

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