vaglio — guardia costituzionale e giudice teleologicoIl Vaglio è il componente che decide se un'azione — proposta spontaneamente da myc o richiesta dall'utente — può procedere. Si chiama Vaglio perché fa esattamente quello: setaccia, separa. Non perché giudichi le persone, ma perché applica due criteri distinti (costituzionale e teleologico) con nature opposte.
Il Vaglio risolve un problema preciso: un LLM che ha generato un output
tende a valutarlo in modo conforme — è il self-enhancement bias
documentato nella letteratura LLM-as-Judge (Zheng et al. 2023, e altri). Se
lasciassimo al medesimo LLM che ha prodotto una ActionProposal il
compito di valutarla contro i telos, avremmo un giudice che tifa per sé
stesso. Il Vaglio impone l'indipendenza del valutatore dal
proponente: contesto separato, prompt separato, possibilmente provider
separato.
expected_alignment.Il nome importa: in italiano vagliare significa separare con un setaccio. L'atto è meccanico, neutro, antico. Non giudica le persone, separa le cose.
Abbiamo scartato cinque alternative, ognuna per un motivo:
| Candidato | Perché scartato |
|---|---|
Arbitro | Sportivo. Dirime partite, non soppesa. Fuori registro. |
Giudice | Antropomorfico. Le 4 Leggi e i telos non processano una persona. |
Critico | Ha un significato tecnico preciso in RL (actor-critic) che confonderebbe i lettori. |
Stadera | Pesa soltanto. Non cattura la dimensione binaria della guardia. |
Super-io | Freudiano. Aggiunge complessità antropomorfa inutile a un design già complesso. |
Vaglio fa entrambe le cose: separa grano da pula (guardia binaria) e distribuisce il grano secondo qualità (giudice graduato). È il gesto, non la persona.
Le fasi sono sequenziali e di natura opposta.
I tre stati categoriali — contraddizione, discrepanza, divergenza — vivono in questa figura:
| Stato | Cosa significa | Dove è visto | Cosa succede |
|---|---|---|---|
| Contraddizione | Un articolo della Costituzione è violato | Uscita dalla Guardia (fase 1) | BLOCK. Il Giudice non vota. Silenzio assoluto sul contenuto. |
| Discrepanza | L'azione non è contraria ai telos, ma neanche allineata | Uscita dal Giudice (fase 2) con expected_alignment ≈ 0 |
REJECT a digest, se proattiva. Normale se reattiva. |
| Divergenza | Tende verso un telos ma in tensione parziale con un altro | Uscita dal Giudice con fit misti |
Pesi decidono; normale e inevitabile. La teleologia qui è nel suo dominio naturale. |
La Guardia non giudica. Verifica. Il suo output è binario:
contradiction: bool.
Operativamente, richiama lo stesso preflight costituzionale già definito in policy §3 Check 0, ma lo isola in una chiamata dedicata al Vaglio. Questa ridondanza apparente è voluta: la Policy può fare cose aggiuntive (rate-limit, never-list, budget) che nulla hanno a che vedere con la Costituzione. Il Vaglio invece è il luogo canonico dove la Costituzione entra.
async def constitutional_guard(
action: ActionLike, # PlannedAction (reattivo) | ActionProposal (proattivo)
constitution: Constitution,
) -> ConstitutionalVerdict:
"""
Verifica contraddizione articolo-per-articolo.
È deterministica e non invoca LLM: le 4 Leggi sono regole codificate.
"""
for article in constitution.articles:
if article.contradicted_by(action):
return ConstitutionalVerdict(
passes=False,
contradicted_article=article.id, # "law.0.perimetro", ecc.
reason=article.explain(action),
)
return ConstitutionalVerdict(passes=True)
Non c'è LLM qui. Le 4 Leggi sono regole eseguibili (espressioni che ispezionano
action.kind, action.targets, action.args).
Questo rende la Guardia veloce (< 1 ms), deterministica e non manipolabile da
prompt injection. La Costituzione non è un LLM; è un filtro deterministico.
law.0.network_out: se
network_out e il target non è in whitelist, contraddizione" non si
lascia convincere. Vedi constitution per lo
schema delle regole.
Il Giudice invoca un LLM indipendente per stimare
fit della azione rispetto a ciascun telos. È un'implementazione di
telos §4 funzione di allineamento, centralizzata qui
nel Vaglio per garantire indipendenza dal proponente.
Il system-block è un template cacheabile che riceve:
TELOS.md (i fini ultimi dell'utente, con note)fit[i] ∈ [0,1] per ogni telos + confidence globaleIl prompt del Giudice non ha accesso alla ExecutionTrace
della ActionProposal, né agli step intermedi del proponente. Vede solo l'output
finale. Questo è il presidio operativo dell'indipendenza.
class JudgeInput:
action_artifact: dict # proiezione "compatta" dell'azione
telos_md_body: str # TELOS.md as-is
# NIENTE DI PIÙ. Non trace, non ragionamento, non proposal_source.
class JudgeOutput:
per_telos: list[FitEstimate] # fit + why per ciascun telos
overall_confidence: float
overall_why: str
La formula è quella definita in telos §4:
expected_alignment = ( Σᵢ weightᵢ · gate(fitᵢ, sogliaᵢ) ) · urgency · confidence − bother_cost
Qui il Vaglio applica:
weightᵢ = peso_effettivo (dichiarato × EMA) da TelosStore+TrustStore.gate() = la funzione-soglia di attivazione per-telos.urgency stimata dal proponente (non dal Giudice): una scadenza non si inventa.confidence dal Giudice stesso.bother_cost dal BotherBudget (telos §5).Se expected_alignment ≥ θ → PUBLISH.
Altrimenti REJECT nel journal.
L'indipendenza è il punto tecnico del Vaglio. Senza indipendenza, il Vaglio è teatro.
ActionProposal come dict compatto (kind, scope, impact, plan, reversibility, rationale).TELOS.md (lo stesso che lo scriverebbe l'utente).urgency numerica.ExecutionTrace che ha prodotto la proposta.ActionProposal è
serializzabile e riproducibile in contesto vuoto. Questo è anche un test di
conformità: se cambi — o oscuri — la storia della proposta, il Giudice
deve produrre lo stesso output, modulo stocastico LLM. Se no, c'è leak.
Il Vaglio non impone un modello. Ogni ruolo è configurabile in config. Solo tre cose sono costanti.
provider, model, tier usati. Sempre.# config.yaml (estratto)
llm_roles:
proposer:
provider: anthropic
model: claude-sonnet-4-6
tier: default
guard: # non usato: la Guardia è deterministica
enabled: false
judge:
provider: anthropic # o "openai", "local"
model: claude-haiku-4-5 # default: tier più economico dei proposer
tier: default
# separation_level: "context" (default) | "provider"
separation_level: context # basta contesto separato
# max_tokens: ragionevole per un JSON di 5-7 fit stimate
max_tokens: 1500
temperature: 0.2 # basso: stima ripetibile
Il separation_level distingue due livelli di indipendenza:
| Livello | Garanzia | Costo |
|---|---|---|
context (default) |
Stesso provider/modello, contesto ripulito. Previene leak di ragionamento. | Economico — sfrutta cache di system block. |
provider |
Provider diverso (es. proposer su Anthropic, judge su modello locale). Previene anche bias familiare al modello. | Maggiore — cache miss; due contratti provider. |
separation_level: context.
Si promuove a provider se l'eval mostra che il Giudice approva
sistematicamente di più le proposte prodotte dal proposer sullo stesso modello
rispetto alla ground truth umana (criterio misurabile via
eval).
Il Vaglio si applica a entrambi i flussi, ma asimmetricamente.
| Flusso | Input | Fase 1 Guardia | Fase 2 Giudice |
|---|---|---|---|
| Reattivo | PlannedAction derivata da richiesta utente |
Sì (contraddizione → deny) | No. L'utente ha chiesto; il Telos non valuta richieste esplicite. |
| Proattivo | ActionProposal da reflection / indexer / neurone |
Sì (contraddizione → block) | Sì (score ≥ θ → publish; altrimenti reject) |
Questa asimmetria è il punto ortogonale tra Vaglio e clausola anti-paternalismo di telos §7: il Telos non giudica le richieste dell'utente perché non si giudica l'utente, non perché ci siano dubbi sulla loro bontà. La Guardia sì lo fa, perché la Costituzione non giudica: impedisce.
from typing import Protocol, Literal
from dataclasses import dataclass
from datetime import datetime
from uuid import UUID
ActionLike = "PlannedAction | ActionProposal"
@dataclass(frozen=True)
class ConstitutionalVerdict:
passes: bool
contradicted_article: str | None = None # "law.0.perimetro" se fail
reason: str = ""
@dataclass(frozen=True)
class TeleologicalVerdict:
expected_alignment: float
per_telos: list["FitEstimate"]
confidence: float
bother_cost: float
threshold_used: float
decision: Literal["publish", "reject", "defer_to_digest"]
@dataclass(frozen=True)
class VaglioResult:
action_id: UUID
final_decision: Literal["block", "publish", "reject", "defer", "allow"]
# block: contraddizione costituzionale
# publish: proattiva, score ≥ θ
# reject: proattiva, score < θ
# defer: proattiva, bother_cost troppo alto ora, rimandata al digest
# allow: reattiva, Guardia passata (il Giudice non vota)
guard: ConstitutionalVerdict
judge: TeleologicalVerdict | None # None se block o allow
llm_config_used: dict # provider, model, tier — per audit
at: datetime
class Vaglio(Protocol):
async def evaluate(
self,
action: ActionLike,
flow: Literal["reactive", "proactive"],
urgency: float = 1.0,
) -> VaglioResult:
"""
Orchestrа Guardia (sempre) e Giudice (solo proattivo).
Ritorna una decisione unica tracciata.
"""
...
def audit_signature(self) -> str:
"""Hash della configurazione LLM corrente. In ogni audit record."""
...
| Eccezione | Quando |
|---|---|
GuardEvaluationFailed | Errore tecnico nel match articolo-per-articolo. Fail-close: equivale a contraddizione (block). Mai fail-open. |
JudgeLLMUnavailable | Provider del Giudice down. Proposte proattive accumulate fino a recovery o timeout 30 min. Reattive passano (la Guardia è sufficiente). |
JudgeOutputMalformed | JSON non valido dopo 2 retry. Proposta rejected con reason="judge_malformed". Non block: non è colpa della proposta. |
SeparationBreach | Tentativo di passare al Giudice dati oltre l'artefatto (trace, ragionamento). Hard failure in test, warning in produzione. |
| Alternativa | Stato | Motivo |
|---|---|---|
| Stesso LLM proposer + self-critique nel prompt | scartato | Self-enhancement bias documentato. Il proposer valuta sé stesso in modo conforme. Soluzione più economica ma inefficace. |
| Panel di N LLM che votano | rimandato | Robusto ma costoso (N chiamate). Da valutare in fase 5-6 se il Giudice singolo mostra varianza eccessiva. |
| Giudice deterministico a regole | scartato | I telos sono frasi ambigue per costruzione ("liberare il mio tempo"). Un motore a regole non può stimare il fit. LLM necessario. |
| Guardia con LLM | scartato | Vulnerabile a prompt injection. Le Leggi devono essere invariabili al fraseggio. Deterministica sempre. |
| Un unico scalare Costituzione+Telos | scartato | Implica che la Costituzione giudichi. Ma la Costituzione non giudica: verifica o blocca. Scale incompatibili, non si sommano. |
| Caso | Verifica |
|---|---|
| Proposta che viola Legge 0 | VaglioResult.final_decision = "block". Giudice non invocato (audit verifica assenza di chiamata LLM judge). |
| Proposta che allinea i telos > θ | final_decision = "publish", per_telos non vuoto. |
| Proposta che allinea i telos < θ | final_decision = "reject", record in telos_rejected.jsonl. |
| Richiesta reattiva legittima | final_decision = "allow". Judge == None. Audit conferma Giudice non chiamato. |
| Richiesta reattiva che contraddice Legge 1 | final_decision = "block", anche se l'utente l'ha chiesta esplicitamente. |
| Tentativo di leak (trace passata al Giudice) | SeparationBreach in test. In prod, warning + strip automatico. |
| Proposta identica valutata due volte di fila | Risultati entro varianza LLM attesa (diff < 10% su expected_alignment). Se no, investigate. |
| Configurazione LLM cambia a runtime | audit_signature() cambia. I record successivi hanno la nuova firma. Niente retroattivo. |
"Anche se non intelligente, un LLM tende a valutare i propri risultati in modo conforme: se è arrivato a un determinato output inevitabilmente tenderà a valutarlo positivamente. Serve un punto di vista e di valutazione separato, per quanto possibile, da quello di chi ha proposto o agito una azione. Una sorta di super-io che ha come asse sia la costituzione che le teleologie e che valuti in negativo lo scostamento dalla costituzione e in positivo l'allineamento con le teleologie, con una chiara definizione di un limite invalicabile: la costituzione in caso di contraddizione ha la prevalenza sulla teleologia. Parlo di contraddizione e non di discrepanza o di divergenza, perché queste ultime due sono inevitabili."Un secondo passaggio ha affinato il principio centrale:
"La Costituzione non giudica, la teleologia sì."Da questa frase discende la struttura: due fasi di natura opposta, non un'unica funzione di valutazione composita. La Guardia verifica (binario); il Giudice misura (gradiente).
La scelta del nome Vaglio è del 22 aprile 2026: sportivo ("arbitro") e antropomorfico ("giudice", "super-io") sono stati esplicitamente scartati per preservare il design da complessità inutile.
llm_roles.fit contro questi fini.
myclaw — vaglio v1.0 — 22 aprile 2026
La Costituzione non giudica, la teleologia sì.