← Indice documentazione Microprogettazione › rl_offline

myclaw

rl_offline — autosviluppo senza training
Microprogettazione v1.0 — 22 aprile 2026
Doc trasversale di Livello 2.5. Formalizza 3 esperimenti ispirati a MiniMax M1 / M2,
DeepSeek-R1, Qwen3, Kimi K2 — ma senza gradient training.
Principio guida: autonomia trust-weighted dalle trace, zero GPU.

Pubblico: chi implementa synthesizer, synapse, policy. Lettura: 20 min.

Indice

  1. Scopo e confini
  2. Perché offline: il vincolo di fase
  3. L'asse unificante: autonomia trust-weighted
  4. Esperimento A — Trace-based reflection scoring
  5. Esperimento B — Synthetic reward nello stadio eval del synthesizer
  6. Esperimento C — Neuron self-play bandit-style
  7. Architettura: il componente TrustStore
  8. Contratto Python
  9. Alternative scartate (ciò che MiniMax fa e noi no)
  10. Test di conformità
  11. Riferimenti

1. Scopo e confini

rl_offline raccoglie le tecniche con cui myclaw diventa più autonomo e meno gravoso per l'utente, usando solo le trace già prodotte, senza gradient training, senza GPU cluster, senza dataset esterni. Le idee sono ispirate alla letteratura RL agentica 2025 (MiniMax M1/M2 CISPO, DeepSeek-R1 GRPO, Qwen3 thinking/non-thinking, Kimi K2 self-critique), ma adattate al vincolo single-user home agent.

Cos'è

Cosa non è

2. Perché offline: il vincolo di fase

I modelli contemporanei citati sopra si addestrano con tecniche di RL su milioni di trace, migliaia di GPU, dataset curati. myclaw non ha nulla di tutto questo e non dovrebbe averlo in fase 1-6. Ma ha invece qualcosa che la letteratura di modelli general-purpose non ha:

Queste asimmetrie permettono qualcosa che è inutile per un modello general-purpose ma prezioso qui: score per-coppia (attore, classe d'effetto), aggiornati in continuo, leggibili e debuggabili. Niente deep learning. Solo media ponderata, decay esponenziale, bandit update.

DECISIONE v1 (scope fase 1-6): myclaw non ospita training on-policy. Tutti i "reward" e "preferenze" vivono come aggregati osservabili di trace. Se mai sarà utile addestrare un modello piccolo locale (es. 1.5B) su trace myclaw, sarà una decisione di fase >6 e merita un doc dedicato. Non qui, non ora.

3. L'asse unificante: autonomia trust-weighted

I tre driver del progetto — autonomia, autosviluppo, carico di approvazione limitato, potenza agentica — sembrano indipendenti ma sono la stessa cosa vista da quattro angoli. La leva che li muove insieme è lo score di fiducia:

ExecutionTrace steps, cost, outcome Approval history granted / denied / undo Constitution events ViolationReport (law.0-3) Synthesizer runs neuron eval results TrustStore aggrega: media ponderata + decay Ebbinghaus produce TrustScore per (subject, effect_class) Policy (A) approve_required → allow Memory reflection (A) promozione episodic→semantic Synthesizer (B) synthetic reward stadio eval Synapse (C) pesi grafo neuroni (self-play) esp. A esp. A esp. B esp. C Un solo store, quattro consumatori. I tre esperimenti corrispondono ai tre flussi verso destra.
Figura 1 — L'architettura. Le fonti a sinistra sono artefatti già persistiti dal resto del sistema; TrustStore è il solo componente nuovo. I quattro consumatori a destra sono tutti esistenti: rl_offline li alimenta, non li rimpiazza.

Ogni esperimento nei paragrafi successivi è un modo di alimentare o consumare il TrustStore. Il diagramma è volutamente spoglio: niente training loop, niente gradient descent, niente buffer di replay. Solo lettura dalle trace e scrittura nelle aggregate.

4. Esperimento A — Trace-based reflection scoring

ESPERIMENTO A

Ogni ExecutionTrace chiusa diventa un vettore di segnali che aggiorna 2 TrustScore.

Al termine di un turno (trace chiusa con qualunque outcome), il sistema estrae 5 segnali per ogni ActionStep registrato e li aggrega in uno update incrementale.

Segnali estratti da una trace

SegnaleEstrazioneRange
tool_success 1 se ActionStep.exec_result è non-None e non contiene ToolError; 0 altrimenti. {0, 1}
cost_efficiency clip(1 − trace.cost_usd / cost_median_per_class, 0, 1). Il mediano per classe è ricalcolato giornalmente. [0, 1]
constitution_clean 1 se nessun ViolationReport generato durante la trace; 0 se almeno una violazione law.0-2; 0.5 se solo law.3. {0, 0.5, 1}
human_approval_delta Se la trace ha generato un Approval: +1 se granted, 0 se denied. Se non c'è stata approvazione richiesta: 0.5 (neutro). [0, 1]
undo_flag 1 se una /undo entro 10 minuti dalla trace ha revertito una sua action; 0 altrimenti. {0, 1}

Aggregazione

Per ogni ActionStep nella trace, identifichiamo il subject:

subject = (step.tool_name, effect_class_of(step.args))
# esempio: ("fs_write", "fs_write:~/workspace/notes/*")
# oppure per un neurone:
subject = ("neuron:budget_tracker", "invoke")

Lo TrustStore.update(subject, signals) applica un EMA (exponential moving average) con decay dipendente dal tempo dall'ultimo update:

def update(self, subject: TrustSubject, signals: dict, now: datetime) -> None:
    score = self._scores.setdefault(subject, TrustScore.empty(subject))

    # Decay temporale (Ebbinghaus-like, half-life 30 giorni di default)
    dt_days = (now - score.last_updated).total_seconds() / 86400
    alpha = 0.5 ** (dt_days / 30.0)                # più vecchio → peso più basso
    fresh_weight = 1 - alpha                       # quanto conta il nuovo segnale

    # Update per componente
    score.tool_success = score.tool_success * alpha + signals["tool_success"] * fresh_weight
    score.cost_efficiency = score.cost_efficiency * alpha + signals["cost_efficiency"] * fresh_weight
    score.constitution_clean = score.constitution_clean * alpha + signals["constitution_clean"] * fresh_weight
    score.human_approval_rate = score.human_approval_rate * alpha + signals["human_approval_delta"] * fresh_weight
    score.undo_rate = score.undo_rate * alpha + signals["undo_flag"] * fresh_weight

    # Aggregato (pesi definiti in types §4.2)
    score.score = (
        0.30 * score.tool_success
      + 0.25 * score.human_approval_rate
      + 0.20 * score.cost_efficiency
      + 0.15 * score.constitution_clean
      + 0.10 * (1 - score.undo_rate)
    )
    score.n_samples += 1
    score.last_updated = now

Consumatori (due, stesso score)

Policy: in evaluate() (policy §3 Check 6), se score.score ≥ threshold.min_score e score.n_samples ≥ threshold.min_samples, il verdetto approve_required viene promosso a allow con via trust_promoted. Ogni promozione è loggata e revocabile con /revoke-trust <subject>.

Memory reflection: nel ciclo di reflection (memory §6), le FactProposal derivate da episodes con subject ad alto score hanno priorità; quelle derivate da subject con score < 0.5 vengono filtrate prima della presentazione all'utente. Questo riduce il rumore: le conversazioni dove il tool è andato male non diventano proposte di memoria semantica.

Perché funziona (ispirazione MiniMax CISPO, DeepSeek-R1 GRPO)

La letteratura RL agentica sopra-citata usa le trace come segnale denso per gradient updates. Qui le usiamo come segnale denso per aggregate updates. La differenza pratica: la loro funzione di merit è parametrica e appresa (policy gradient), la nostra è esplicita e ispezionabile (tabella sopra). Perdiamo espressività, guadagniamo auditabilità totale e nessuna GPU richiesta.

DECISIONE DA CONFERMARE. Il peso 0.30 di tool_success contro 0.25 di human_approval_rate è una scelta calibrata per evitare il sycophancy loop: un neurone che sa chiedere belle approvazioni non deve contare più di un neurone che produce risultati validi. Se in uso emerge l'opposto, invertire. Flaggare il pattern in observability dopo il primo mese di raccolta dati.

5. Esperimento B — Synthetic reward nello stadio eval del synthesizer

ESPERIMENTO B

La pipeline 7-stadi di synthesizer calcola un reward composito prima del gate umano. Se sotto soglia, il sintetizzatore riprova; se sopra, chiede l'approvazione con un dossier già informato.

Oggi (synthesizer.html §5) lo stadio di test di nascita produce un verdetto binario: il neurone passa o no. L'esperimento B lo estende a uno score continuo — stesso costo di calcolo, molto più informativo.

Composizione del reward

R(neuron_candidate) =
      w1 * det_pass_rate          # % test deterministici verdi
    + w2 * judge_score            # LLM-as-judge (local-fast) su rubrica costituzionale
    + w3 * cost_ratio             # costo sandbox effettivo vs stima (budget)
    + w4 * similarity_penalty     # penalità se troppo simile a neurone esistente
    + w5 * coverage_bonus         # bonus se copre effect_class non coperto

dove w = (0.40, 0.25, 0.15, −0.10, 0.10)  # somma = 0.80; soglia gate = 0.65

Loop del sintetizzatore

  1. Genera bozza del neurone (stadi 1-4 di synthesizer).
  2. Stadio 5 (eval): esegue dry_run + test deterministici in sandbox; calcola R.
  3. Se R >= 0.65: lo stadio 6 presenta all'utente il dossier con R breakdown visibile. L'utente approva, nega, o chiede revisione.
  4. Se R < 0.65: il sintetizzatore riceve il R breakdown come feedback in-context nella prossima iterazione: "La bozza ha fallito det_pass_rate=0.40 perché test 2 e 5 sono rossi; il giudice ha segnalato law.1 borderline; riprova tenendo conto di questi".
  5. Max 3 retry (come definito in synthesizer §6). Dopo 3 fallimenti, la pipeline si ferma e chiede aiuto all'utente.

Perché non è gradient learning

Il feedback all'LLM è prosa in context, non gradiente. La pipeline è una forma di rejection sampling con feedback testuale. È esattamente ciò che DeepSeek-R1-Zero fa nei suoi primi stadi (prima del puro RL su preferenze) e ciò che Kimi K2 fa con il suo self-critique. Non serve addestrare nulla: serve solo un prompt che include R breakdown.

Il gate umano resta obbligatorio

Principio vincolante. Nessun neurone entra in produzione senza Approval esplicita di Roberto, nemmeno con R = 1.0. Il reward sintetico riduce il carico cognitivo del gate umano (Roberto vede già lo score e il breakdown, quindi decide più in fretta), ma non lo sostituisce. Questa è una conseguenza diretta della Legge 2 costituzionale (non auto-promuovere).

Integrazione col TrustStore

Una volta che il neurone è approvato e attivo, il suo R iniziale diventa il primo campione per il TrustScore del subject ("neuron:<name>", "invoke"). Questo lo "boota" con una storia non a zero: un neurone ben-testato parte con fiducia parziale, un neurone borderline parte con fiducia bassa — e l'esperimento A aggiornerà da lì.

6. Esperimento C — Neuron self-play bandit-style

ESPERIMENTO C

Due neuroni pertinenti allo stesso task eseguono in parallelo su una query; un terzo neurone giudice (modello locale economico) ranka i risultati. L'esito aggiorna i pesi nel grafo synapse.

Lo scopo è risolvere un problema reale: quando più neuroni coprono aree sovrapposte (es. budget_tracker vs finance_assistant), il grafo synapse fatica a decidere quale richiamare. Il bandit-update offre un criterio comportamentale: chi produce il risultato giudicato migliore da un critic asimmetrico vince il peso.

Protocollo

  1. Il runtime riceve una query che, secondo synapse.neighbors(context), ha almeno 2 neuroni candidate con peso comparabile (differenza < 0.15).
  2. Entrambi i neuroni vengono invocati in parallelo (max wall_time = timeout normale del turno). Questo raddoppia il costo di quel turno; la Policy deve approvarlo se il budget lo permette (vedi vincolo sotto).
  3. Un terzo neurone, il critic, è invocato con:
    • la query originale,
    • i due risultati in formato identico (order randomizzato per ridurre position bias),
    • la rubrica: costituzione render + criteri di qualità task-specific.
    Il critic gira su modello local-fast (cost tier basso, vedi policy §6).
  4. Il critic ritorna un ranking (winner, loser) + giustificazione testuale.
  5. Il runtime consegna all'utente il risultato winner. Il loser non viene mostrato ma è loggato per audit.
  6. Bandit update nel synapse: w[winner] += η, w[loser] -= η/2, con η = 0.05 e clipping a [0, 1]. Asimmetrico: vincere porta più peso che perderne.

Vincoli di attivazione (quando NON fare self-play)

Perché bandit e non ranking multi-episodio

Il bandit update è semplice, incrementale, senza memoria. Un algoritmo più sofisticato (es. ranking con Elo, o Bayesian preference learning) darebbe score più ricchi, ma richiederebbe buffer di replay e re-training periodico — contraddice il vincolo "no training" (§2). Il bandit fa peggio ma è robusto all'ingresso di nuovi neuroni, resistente al drift di distribuzione, e non richiede mai un "rebuild".

Ispirazione Kimi K2 self-critique. Kimi K2 addestra il modello principale con self-critique come segnale di reward. Noi non addestriamo, ma usiamo lo stesso pattern architetturale: due generatori, un critico, un update del peso. L'analogia è esatta nella forma, diversa nella sostanza (bandit update vs gradient update).

Il critic è asimmetrico e deve restarlo

Il critic è sempre un modello diverso dai due neuroni competitor. Usare uno dei due come critic sarebbe barare: valuterebbe se stesso. In fase 1 il critic è semplicemente il modello local-fast configurato (cost tier basso) con prompt dedicato. In futuro (fase 7+) può diventare un neurone specializzato critic_neuron, ma con la stessa regola: mai usare un neurone come critic di se stesso.

7. Architettura: il componente TrustStore

Il TrustStore è l'unico componente nuovo introdotto da questo doc. Vive in src/myclaw/rl_offline/trust.py. Espone un'API di lettura (veloce, in-memory) e una di scrittura (batched, persistente).

Persistenza

workspace/state/trust.db              # SQLite
  └─ scores(subject_hash, tool_name, effect_class, component_blob, last_updated)
  └─ thresholds(effect_class, risk_band, min_score, min_samples, cap_autonomy)
  └─ events(ts, subject_hash, trace_id, delta_blob)  # append-only, per replay

component_blob è un JSON delle 5 componenti + score aggregato. events è l'audit log dei delta applicati: permette di ricostruire la storia e, se serve, rollback di uno score (es. dopo scoperta di un abuse).

Quando viene scritto

TriggerCosa produce
Trace chiusa (qualunque outcome) Esp. A: update di 1 TrustScore per ogni ActionStep unico, più 1 globale per il subject ("runtime", outcome).
Approval concessa / negata Esp. A: update del segnale human_approval_delta sulla trace di origine.
/undo eseguito Esp. A: flag undo_rate sulla trace originaria.
Synthesizer chiude un neurone con R score Esp. B: scrittura iniziale di TrustScore per ("neuron:<name>", "invoke") con score=R, n_samples=1.
Self-play completato Esp. C: writeall su synapse edge weights; aggiornamento componente tool_success del winner.
Job notturno (reflection cycle, ~03:00) Ricalcolo mediani cost per classe (usato da esp. A segnale cost_efficiency); decay naturale degli score non toccati da >30g.

Quando viene letto

ChiamanteMetodo
Policy Check 6 (§cap3) store.get(subject) → TrustScore (O(1) hash lookup).
Memory reflection filter store.get(subject).score per filtrare FactProposal.
Synapse edge weight compute store.get_for_neuron(name) per compute decay + weight.
Observability dashboard store.snapshot() per leaderboard neuroni e top drift.

8. Contratto Python

from dataclasses import dataclass
from datetime import datetime
from typing import Protocol, Literal
from uuid import UUID

from myclaw.types import (
    ExecutionTrace, TrustScore, TrustThreshold, TrustSubject,
    Approval, ViolationReport,
)

# --- Signals estratti da una trace (esp. A) ---

@dataclass(frozen=True)
class TraceSignals:
    """Output di extract_signals(trace). Una riga per ActionStep."""
    subject: TrustSubject
    tool_success: float           # {0, 1}
    cost_efficiency: float        # [0, 1]
    constitution_clean: float     # {0, 0.5, 1}
    human_approval_delta: float   # [0, 1], default 0.5 se no approvazione
    undo_flag: float              # {0, 1}

# --- Store ---

class TrustStore(Protocol):
    async def get(self, subject: TrustSubject) -> TrustScore | None:
        """O(1). None se subject non ha storia."""
        ...

    async def update(
        self,
        subject: TrustSubject,
        signals: TraceSignals,
        now: datetime,
    ) -> None:
        """EMA update con decay Ebbinghaus (half-life 30g)."""
        ...

    async def bulk_update_from_trace(self, trace: ExecutionTrace) -> None:
        """Esp. A: un update per ogni ActionStep unico nella trace."""
        ...

    async def record_approval_outcome(
        self,
        trace_id: UUID,
        approval: Approval,
    ) -> None:
        """Quando l'approval arriva asincronamente, chiude il delta."""
        ...

    async def record_undo(self, trace_id: UUID) -> None:
        """Quando /undo reverte un'azione, alza undo_flag per i subject."""
        ...

    async def snapshot(self) -> dict[TrustSubject, TrustScore]:
        """Per observability dashboard / leaderboard."""
        ...

    async def rollback(self, subject: TrustSubject, to: datetime) -> None:
        """Ricostruisce lo score allo stato di prima di to,
        replayando gli events dall'append-only log. Usato se uno score
        è stato corrotto (es. abuse detected)."""
        ...

# --- Thresholds ---

class TrustThresholdRegistry(Protocol):
    def for_effect(
        self,
        effect_class: str,
        reversibility: Literal["reversible", "reversible_with_cost", "irreversible"],
    ) -> TrustThreshold:
        """Ritorna la soglia di promozione per (effect_class, reversibility).
        Le azioni irreversibili hanno sempre min_score=1.0 (mai promuovibili)."""
        ...

# --- Self-play coordinator (esp. C) ---

@dataclass
class SelfPlayResult:
    winner: str                       # neuron_name
    loser: str
    critic_verdict: str               # giustificazione testuale
    critic_cost_usd: float
    users_accepted_winner: bool | None   # se il turno ha avuto feedback

class SelfPlayCoordinator(Protocol):
    async def maybe_run(
        self,
        query: str,
        candidates: list[str],        # neuron names with comparable synapse weights
        trace: ExecutionTrace,
    ) -> SelfPlayResult | None:
        """Ritorna None se i vincoli di attivazione (cap6) non sono soddisfatti."""
        ...

Errori sollevabili

EccezioneQuando
TrustCorruptionErrorIl component_blob letto non è coerente (checksum fallito o valori fuori range). Il runtime procede con TrustScore.empty() (= nessuna fiducia) e logga evento critico.
ThresholdNotFoundErrorPolicy chiede una soglia per un effect_class sconosciuto. Fallback conservativo: min_score=1.0 (mai promuovere).
SelfPlaySkippedNon è un errore ma un segnale: SelfPlayCoordinator.maybe_run() ha deciso di non attivarsi. Runtime usa il neurone col peso maggiore.

9. Alternative scartate (ciò che MiniMax fa e noi no)

TecnicaChi la usaPerché non la adottiamo
CISPO / GRPO / PPO on-policy MiniMax M1, DeepSeek-R1 Richiede infrastruttura GPU, dataset curato di migliaia di rollout, e — soprattutto — libertà di esplorazione. myclaw è un sistema single-user con azioni a side-effect reali: non possiamo lasciare esplorare a caso per raccogliere traiettorie.
Reward model appreso tutti i modelli con RLHF Richiede migliaia di annotazioni di preferenza. Abbiamo un solo annotatore (Roberto). Rule-based + human-approval già raccolti è sufficiente.
Thinking / non-thinking mode switchabile dal modello Qwen3 Adattato in policy §6 cost_tiering in forma più semplice (la policy sceglie il tier, non l'LLM). La versione Qwen richiede che il modello stesso abbia due modi di inferenza; i nostri LLM di provider non lo espongono in modo uniforme.
Self-play multi-agent come source di training data Kimi K2, varie ricerche 2025 Adattato in forma minimale (esp. C). La versione K2 usa self-play per generare dataset da cui addestrare; noi usiamo solo i pesi bandit, nessun dataset.
Lightning attention 1M context MiniMax-Text-01, M1 Architetturale del modello stesso. myclaw orchestra modelli esterni; non addestriamo né forkiamo il provider.
MoE routing MiniMax, DeepSeek, Kimi Stesso motivo: architettura del modello. Il nostro "routing" è il cost tiering, che gira a livello di workflow (policy).
Replay buffer + experience replay DQN / agentic RL Incompatibile con la privacy single-user: le trace sono personali, non costruiamo un buffer interrogabile fuori contesto. L'append-only journal è consultabile solo per audit, non per training.
Constitutional RL (RLAIF) Anthropic Claude Idea adiacente alla costituzione di myclaw, ma l'enforcement è diretto (Constitution.check() pre-flight, vedi policy §3), non indiretto via reward model. La costituzione è eseguibile, non appresa.

10. Test di conformità

InvarianteTest
TrustScore empty per subject sconosciuto store.get(("neuron:ghost", "invoke")) → None. La Policy interpreta None come "mai promuovere".
EMA con decay half-life 30g Signals a 1.0, nessun update per 30g → score atteso 0.5 ± 0.02.
Azione irreversibile mai promuovibile TrustThreshold per reversibility="irreversible" ha min_score=1.0; test inject score=0.99, atteso verdict=approve_required.
Undo abbassa lo score Trace con tool_success=1, poi record_undo(trace_id) → nuovo score < quello senza undo.
Synthesizer R → initial TrustScore Synthesizer approva neurone con R=0.72; subito dopo store.get(("neuron:name", "invoke")) → score≈0.72, n_samples=1.
Self-play skippato sotto budget Budget rimanente 0.01€, self-play candidato costerebbe 0.05€ → maybe_run() ritorna None, via=budget.
Self-play skippato su side-effect irreversibile Neuroni candidate che toccano telegram_sendmaybe_run() ritorna None.
Critic mai è uno dei competitor Per ogni SelfPlayResult: critic_neuron ≠ winner e critic_neuron ≠ loser.
Rollback è replayabile Applicare 100 update, rollback a T-50, applicare nuovamente 50 update → score identico a quello post-100 (deterministica bit-a-bit).
No promozione di human_approval_rate se nessuna approvazione Trace senza ApprovalRequest → human_approval_delta=0.5 (neutro), non influenza score verso l'alto.
Constitution clean penalizza law.0-2 Trace con violation law.1 → constitution_clean=0. Dopo 100 trace clean, score torna > 0.9.
Append-only events Ogni update produce una riga in events table; nessuna mai riscritta. SELECT COUNT(*) FROM events cresce monotona.

11. Riferimenti

Il razionale "perché senza training" è nei doc di Livello 1. I riferimenti qui sono solo i lavori agentici rilevanti per le adattazioni specifiche di myclaw.

RiferimentoCosa abbiamo preso
MiniMax M1 (CISPO, 2025) L'idea di usare trace a lunga traiettoria come segnale denso. Noi aggreghiamo invece di addestrare (esp. A).
DeepSeek-R1 (GRPO + rule-based reward) La lezione "reward verificabile > reward model appreso". Usiamo rule-based in esp. B e C.
Qwen3 (thinking / non-thinking) Il cost tiering a due livelli (vedi policy §6). Il gate è fatto dalla policy, non dal modello.
Kimi K2 (self-critique) Il pattern architetturale generator × generator × critic di esp. C. Noi chiudiamo con bandit update, non gradient.
CoALA (Sumers et al. 2023) La reflection ciclica e la memoria a tre livelli che rende possibile estrarre segnali (§4).
Ebbinghaus forgetting curve / ACT-R Il decay temporale half-life 30g (esp. A aggregation).
Bandit algorithms (UCB, classical) L'aggiornamento asimmetrico dei pesi del grafo synapse (esp. C).
myclaw revisione incrociata di fase 1 La necessità del TrustScore come asse unificante dei 4 driver.

Continua a leggere

tipi condivisi
types.html
Definisce TrustScore, TrustThreshold, PlannedAction. Prerequisito di lettura.
consumatore A
policy.html §3
Dove il TrustScore promuove approve_requiredallow (Check 6).
consumatore B
synthesizer.html §5
Dove il synthetic reward R informa il dossier di approvazione per nuovi neuroni.
consumatore C
synapse.html
Dove i pesi del grafo sono aggiornati dal bandit self-play (esp. C).

myclaw — rl_offline microprogettazione v1.0 — 2026-04-22
Doc trasversale di Livello 2.5. Formalizza i 3 esperimenti MiniMax-inspired senza training.