# 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.

Published: 2026-10-02
Canonical: https://gianlucamazza.it/it/blog/mklang
Tags: mklang, LLM, DSL, State-Machine, Agents

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](/it/progetti#mklang). Il repository è [gianlucamazza/mklang](https://github.com/gianlucamazza/mklang), Apache-2.0. Spec del linguaggio 0.4. Pacchetto di riferimento 1.3.7. Documentazione su [docs.mklang.dev](https://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](/it/blog/langgraph-workflow-orchestration) è 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.

```plaintext
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](/it/blog/memory-architectures) 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`, `balanced` o `reasoning`. 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](/it/blog/ai-workflow-engines).

La macchina più piccola:

```plaintext
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.

```plaintext
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](/it/blog/bank-grade-agent-evals) 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 `structure` né 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](/it/blog/langgraph-workflow-orchestration) 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](https://github.com/gianlucamazza/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](/it/progetti#mklang), poi [SPEC.md](https://github.com/gianlucamazza/mklang/blob/main/SPEC.md), poi `hello.mkl` da `mklang init --user`. La guida della console è `docs/guides/console.md` nel repo.
