mklang: il documento è il programma
Un file .mkl dichiarativo per macchine a stati LLM. I modelli generano. La macchina decide cosa succede dopo. Cosa il linguaggio garantisce e cosa no.
Ho tradotto questo articolo dall’inglese con un modello linguistico. Leggi l’originale in inglese
Un file .mkl descrive un agente come una macchina a stati. Un LLM esegue i passi generativi. Il documento è il programma; l’host fornisce l’interprete, i tool e i hook gate in codice.
I modelli generano. Le macchine decidono cosa succede dopo.
La scheda progetto è su Progetti. Il repository è gianlucamazza/mklang, Apache-2.0. Spec del linguaggio 0.4. Pacchetto di riferimento 1.3.7. Documentazione su docs.mklang.dev.
Perché conta
Ho messo in produzione control flow di agenti in Python. Dopo pochi mesi il grafo è reale, ma vive nei constructor, nelle closure e nel provider con cui il processo era configurato. Una diff sul comportamento è una diff sul codice dell’applicazione. Va bene per un’applicazione. È un artefatto cattivo quando quello che voglio ispezionare, versionare e consegnare è il control flow stesso.
LangGraph è lo strumento giusto quando il grafo è codice di applicazione. Uso ancora quella forma. mklang è l’altro lato dello stesso problema: un documento portatile, interpretato da un runtime LLM, con la topologia scritta.
mklang : LangGraph :: a declarative spec : Python code
Lo scambio è esplicito. Ottieni un programma indipendente dal provider e un control flow che si legge senza l’interprete. Rinunci all’espressività del linguaggio host. Le macchine in produzione servono ancora uno sviluppatore per tool, hook, budget e input non fidati.
Non pretendo adozione, un benchmark, o che due modelli prendano lo stesso gate. Il risultato utile è il vincolo: una diff sul .mkl è una diff sulla macchina.
Che cos’è una macchina
Quattro entità.
- machine — un file
.mkl. Stato di entry, budget globale, mappa degli stati. - state — dove succede qualcosa.
- context — una blackboard che accumula durante il run. L’unico canale di memoria tra stati. L’output di uno stato viene depositato sotto una chiave; i prompt lo leggono con
{{…}}. - tier —
fast,balancedoreasoning. Capacità, non vendor.
Fissare un provider o un modello dentro il documento è un non-obiettivo deliberato. Romperebbe la portabilità. L’host mappa i tier su modelli concreti in runtime.yaml. Lo stesso file gira su DeepSeek, Anthropic, OpenAI, Google, OpenRouter, xAI, Mistral, o un endpoint locale senza chiave come Ollama.
Ogni stato generativo ha quattro facce.
| Face | Domanda | Regola |
| ----------- | ----------------- | ------------------------------------------------------------- |
| structure | Che forma? | Prosa, non un tipo |
| prompt | Che cosa pensare? | Task, con interpolazione {{…}} |
| execution | Come agire? | Policy sticky. Mai un effetto collaterale |
| gates | Quando uscire? | Condizioni in linguaggio naturale. Queste sono le transizioni |
Un gate si risolve in ok, repair, escalate o fail, poi instrada. Gli effetti collaterali non sono prosa. Uno stato tool: chiama un callable dell’host; l’osservazione rientra nel context. Facce opzionali coprono i pattern che uso davvero: reason per una chain of thought tracciata, accumulate per append su lista, sample / over per fan-out, call per un’altra macchina. Un budget di step ferma un loop fuori controllo. I checkpoint sono riprendibili.
La macchina più piccola:
machine: greet
entry: answer
states:
answer:
prompt: "Greet the user in one sentence."
output: reply
gates:
- when: the reply is a greeting
then: ok
to: END
Il modello produce reply. Un judge decide se è un saluto. La topologia è nel file, non in un system prompt.
Il pattern cookbook della spec mappa le forme usuali di agente su questo nucleo: chain-of-thought, ReAct, Reflexion, self-consistency, tree-of-thought, plan-and-execute, debate, map-reduce, router, speculative cascade. examples/ ha react.mkl, triage.mkl e self_consistency.mkl. Una piccola standard library (std_self_consistency, std_refine e altre) è invocabile dalla CLI o dalla console.
Che cosa fa l’host
Il documento non compila. Un runtime conforme è un host qualsiasi con accesso a un LLM. L’interprete di riferimento è Python.
pipx install 'mklang[mcp]'
mklang init --user
mklang console
init --user crea config e .env sotto ~/.config/mklang/, e le macchine sotto ~/.local/share/mklang/machines/, senza sovrascrivere. La console è la porta d’ingresso: una TUI agent-first il cui cervello è a sua volta una macchina, agent.mkl, senza poteri privilegiati. Le sessioni persistono (--continue). Escalation e consenso sui tool sono interattivi. Il cervello di default legge il workspace tramite tool in sola lettura, delimitati. Niente shell, niente write.
mklang check valida. mklang doctor dice quale layer di config ha vinto, quali chiavi mancano e quali backend di tool sono vivi. mklang test esegue una macchina contro scenari nominati con un LLM scriptato, così un fixture di control flow non ha bisogno di una API key. La suite di conformance è YAML indipendente dall’implementazione. Un secondo runtime, in TypeScript o Rust, è conforme se passa gli stessi casi. La conformance è il contratto meccanico. Non è una promessa che due modelli giudichino un gate allo stesso modo.
La validazione in editor dipende dallo JSON Schema pubblicato:
https://raw.githubusercontent.com/gianlucamazza/mklang/main/schema/mklang.schema.json
Che cosa non è
La spec lo elenca di proposito.
- Non compila a un artefatto formale.
- Non garantisce determinismo.
- Non ha tipi statici per
structurené per le condizioni dei gate. Entrambi sono prosa, giudicati a runtime. - Accuratezza dei gate e stabilità cross-provider sono empiriche. Ho visto gate divergere tra provider. È una misura, non un bug del documento.
Il context non fidato è delimitato, non giudicato. Input dell’host, osservazioni dei tool e depositi sono tainted. Le interpolazioni tainted sono recintate come <data-NONCE>…</data-NONCE> con un nonce fresco per chiamata, e al modello viene detto che il contenuto recintato non è un’istruzione. Una decisione di judge su dati esterni marca la transizione. Uno stato tool: con effetti su quel path può essere rifiutato con --untrusted-flow halt. mklang lint nomina quegli stati in fase di scrittura.
La policy esatta non sta su un gate in prosa. Importi e allowlist stanno su un gate hook:, un predicato dell’host senza modello nel path, o su un’escalation prima di un effetto irreversibile. Broker di tool in sandbox, zone di context firmate e prove di non-interferenza sono non-obiettivi espliciti.
Questo repository è il linguaggio e l’interprete di riferimento. Una piattaforma hosted, se esiste più avanti, è un’altra cosa. Non trattare l’albero Apache-2.0 come un orchestrator da mettere in produzione e dimenticare.
Dove sta rispetto all’altro lavoro
LangGraph resta il posto in cui lo stato è un’API: channel, reducer, checkpoint di stato piuttosto che di effetti collaterali. mklang non lo sostituisce. È il documento che voglio quando la macchina deve sopravvivere al Python che capita di eseguirla.
orka è l’altro confine: un runtime Rust con limiti di capability stretti intorno al lavoro multi-agente. mklang è il programma ispezionabile. orka è l’host che deve rendere difficile un effetto non autorizzato. Non sono lo stesso layer, e non pretendo che il file .mkl sia un sistema di capability.
FAQ
mklang sostituisce LangGraph?
No. LangGraph è codice di applicazione che costruisce ed esegue un grafo. mklang è un documento portatile interpretato da un runtime LLM. Usa LangGraph quando ti serve l’espressività del linguaggio host. Usa un .mkl quando il control flow stesso è l’artefatto di cui vuoi una diff.
Un run di conformance verde vuol dire che l’agente è corretto?
No. La conformance fissa la semantica meccanica: interpolazione, routing, budget, marcatura taint. Gli esiti dei gate dipendono dal modello. Due runtime conformi possono divergere in produzione.
Dove vanno gli effetti collaterali?
In uno stato tool:, che è un callable dell’host, o dietro un gate hook: per la policy esatta. Mai in execution, e mai come istruzione in prosa che il modello deve confermare.
Che cosa leggo per primo?
La scheda progetto, poi SPEC.md, poi hello.mkl da mklang init --user. La guida della console è docs/guides/console.md nel repo.
Articoli correlati
Una risposta rifiutata deve comunque addebitare i token
Se il runtime rifiuta la risposta, conservo i token della chiamata produce già fatturata. Onestà del ledger per agenti governabili, da mklang 1.3.7 / pull 111.
4 ott 20268 min di lettura#mklang#LLM#Cost#Agents#ProductionI dati non fidati non devono possedere il flusso di controllo
Mostro come reasoning-kernel tiene i dati non fidati fuori dagli effetti: due reasoner non fidati, un gate deterministico. Topologia, non certificato.
2 ott 20268 min di lettura#Agents#Security#LLM#Python#ProductionDire che un LLM non pensa è come dire che una calcolatrice non sa fare i numeri?
Dove regge e dove si spezza l'analogia tra LLM e calcolatrice: cosa dicono interpretabilità, chain-of-thought e filosofia della mente su "pensare".
2 lug 202616 min di lettura#LLM#AI Reasoning#Interpretability#Philosophy of Mind