Da linguaggio naturale a comando shell con un'API LLM: JSON e costo
Uno script Python che chiede a un modello economico un comando shell come JSON, rifiuta o segnala le richieste incerte, non esegue mai nulla e calcola il prezzo di 1.000 richieste.

In questa pagina
Chiedi al modello un singolo oggetto JSON con tre campi (type, command, explanation), limita la risposta a un paio di centinaia di token e stampa il comando invece di eseguirlo. In termini di token, una richiesta così è quasi tutta prompt con una risposta breve e prevedibile: con le ipotesi qui sotto, 1.000 di queste costano $0.012 su DeepSeek-V4-Flash.
Immagina di aggiungere una casella "descrivi cosa vuoi" a una CLI interna. Qualcuno scrive trova i file più grandi di 100 MB nella mia cartella home, e tu vuoi indietro una riga che possa leggere, controllare e incollare. Seguono due domande: come impedisci al modello di tirare a indovinare o di fare qualcosa di pericoloso, e quanto costa quando cento persone lo usano tutto il giorno?
Un compito ristretto: perché la risposta è breve#
Il 9 novembre 2026 un canale Telegram ha riproposto la model card di dale-0.8b, un modello aperto che fa esattamente questo unico lavoro. Secondo la model card su Hugging Face (verificata il 10 novembre 2026), dale-0.8b è un full fine-tune di Qwen3.5-0.8B sotto Apache-2.0 che trasforma una richiesta in spagnolo o inglese in un singolo comando bash Linux. L'autore, Manuel García, riporta il 79,4% (374 casi su 471) sul proprio test set, contro il 62,8% di Qwen3.5-9B base e di Gemma 4 E4B-it senza fine-tuning. Sono numeri auto-dichiarati su un test set di una sola persona, quindi trattali come un dato, non come una classifica.
Ciò che ci è utile è la forma del compito, non il modello. La card descrive un system prompt fisso, una riga di contesto (OS, distro, package manager, strumenti) più la richiesta, il thinking disattivato e una risposta limitata a 200 token. Quel limite è una delle due leve sul costo: quando la risposta è un breve oggetto JSON, il lato output è breve e prevedibile, perché il limite lo contiene. L'altra leva è il prompt. Quale quota del conto spetti a ciascun lato lo mostra la tabella qui sotto; varia da modello a modello.
Non ti serve un modello da 0.8B per questo. Lo stesso formato funziona su qualsiasi modello API, ed è ciò che costruisce il resto di questo articolo.

Il formato della risposta: comando, rifiuto o incertezza#
Il trucco è che il modello ha sempre un modo legittimo per dire "no". La risposta è un solo oggetto:
{"type": "command | refuse | unsure", "command": "...", "explanation": "..."}command: un comando bash, più una spiegazione di una frase su cosa fa.refuse: la richiesta è distruttiva o non ha nulla a che fare con la shell. Nessun comando.unsure: la richiesta è troppo vaga ("pulisci il mio disco"). La spiegazione contiene una domanda di chiarimento.
Senza unsure, una richiesta vaga obbliga a tirare a indovinare, e un rm indovinato è quello che ti ricordi. Il formato della card ha gli stessi tre tipi; riusiamo l'idea, non il modello.
Abbina le coppie
Abbina ogni tipo di risposta a ciò che lo script deve fare
Tocca un elemento a sinistra, poi il suo abbinamento
- commandMostra il comando e la spiegazione; la persona lo copia e lo esegue da sola
- refuseStampa la spiegazione e non offrire nulla da eseguire
- unsureStampa la domanda e lascia che l'utente la riformuli
Lo script#
Un file, il pacchetto openai, chiave in una variabile d'ambiente. La base URL e l'id completo del modello vengono dai documenti di CheapTokens; gli id brevi non sono accettati, quindi usa deepseek-.
import json
import os
import sys
from openai import OpenAI
client = OpenAI(
base_url='https://api.cheaptokens.dev/v1',
api_key=os.environ['CHEAPTOKENS_API_KEY'],
)
MODEL = 'deepseek-ai/DeepSeek-V4-Flash-0731'
SYSTEM = '''You turn a request into ONE Linux bash command.
Reply with a single JSON object and nothing else:
{"type": "command" | "refuse" | "unsure", "command": "<one command, or empty>", "explanation": "<one short sentence>"}
Use "refuse" when the request is destructive or not about the shell.
Use "unsure" when it is too vague, and put a clarifying question in "explanation".'''
CONTEXT = 'os: Ubuntu 24.04; package manager: apt; tools: git, curl, jq'
def parse(text):
start = text.find('{')
if start == -1:
return None
try:
obj, _ = json.JSONDecoder().raw_decode(text[start:])
except json.JSONDecodeError:
return None
if not isinstance(obj, dict) or obj.get('type') not in ('command', 'refuse', 'unsure'):
return None
return obj
def ask(request):
resp = client.chat.completions.create(
model=MODEL,
max_tokens=200,
temperature=0,
messages=[
{'role': 'system', 'content': SYSTEM},
{'role': 'user', 'content': f'Context: {CONTEXT}\nRequest: {request}'},
],
)
return parse(resp.choices[0].message.content), resp.usage
if __name__ == '__main__':
result, usage = ask(' '.join(sys.argv[1:]))
if result is None:
print('Could not read the reply as JSON. Try again.')
elif result['type'] == 'command':
print(result['explanation'])
print('\n ' + result['command'] + '\n')
print('Read it, then copy it yourself. This script never runs it.')
else:
print(f"[{result['type']}] {result['explanation']}")
print(f'tokens: {usage.prompt_tokens} in, {usage.completion_tokens} out')Eseguilo come python shellhelp.. Tre scelte contano qui:
- Nessun
subprocessda nessuna parte. Il comando è testo sullo schermo. Eseguirlo è una tua decisione, dopo averlo letto. max_tokens=200, come il limite della card. Un oggetto JSON ci sta comodamente; una risposta prolissa viene tagliata a 200 token invece di continuare a tue spese. Il limite si applica all'intera risposta, quindi se un modello scrive il suo ragionamento, questo può consumare il limite. Se una risposta termina confinish_reasonuguale alength, alza un po' il limite. Ne parliamo in ridurre i token di output con max_tokens.- Il formato è richiesto nel prompt, poi analizzato in modo difensivo.
raw_decodesalta un code fence vagante o una frase prima del{. Se vuoi spingerti oltre, vedi JSON valido da un'API compatibile con OpenAI e la soluzione per json.JSONDecodeError su una risposta.
Prima di eseguire qualsiasi cosa, verifica che l'ordine dei passaggi sia chiaro:
Metti in ordine
Metti in ordine corretto i passaggi dello script
Trascina gli elementi o usa le frecce
- Costruisci il prompt: testo di sistema fisso, una riga di contesto, la richiesta
- Chiama l'API con un max_tokens piccolo
- Analizza il JSON e verifica che type sia uno dei tre
- Mostra il comando e attendi che la persona lo esegua
Quanto costano 1.000 richieste#
L'ultima riga dello script stampa usage. Quello è il numero reale, e dovresti leggere il tuo. Finché non ce l'hai, lavora con un'ipotesi: il prompt arriva a circa 400 token di input (il prompt nudo nello script è all'incirca 150 token a un conteggio approssimativo; il resto è spazio per le tre o quattro coppie di esempio richiesta-e-risposta che probabilmente aggiungeresti), e la risposta JSON a circa 60 token di output.
Per 1.000 richieste sono 400 × 1.000 = 400.000 token di input e 60 × 1.000 = 60.000 token di output. La tabella prezza esattamente questo per ogni modello del catalogo, con la colonna OpenRouter accanto; le cifre provengono da prezzi live, quindi restano aggiornate.
| Modello | Input $/1M | Output $/1M | Per richiesta | 1000 richieste | OpenRouter, 1000 richieste |
|---|---|---|---|---|---|
| GLM-5.3-Flash | $0.02 | $0.04 | $0.00001 | $0.0104 | $0.0184 |
| DeepSeek-V4-Flash | $0.015 | $0.10 | $0.000012 | $0.012 | $0.0364 |
| MiniMax-M2.7 | $0.05 | $0.15 | $0.000029 | $0.029 | $0.348 |
400 token di input e 60 token di output per richiesta. I prezzi per 1M di token sono attuali e cambiano insieme alle nostre tariffe; la colonna OpenRouter mostra i prezzi di riferimento per lo stesso carico di lavoro.
In totale, contando insieme input e output, DeepSeek-V4-Flash risulta più economico di OpenRouter per questo carico di lavoro. In termini di token la richiesta è quasi tutta input e la risposta è breve, ma entrambi i lati vengono fatturati, quindi riduci sia il prompt sia la risposta. I prezzi per modello sono sulla pagina dei prezzi, e il modello stesso è descritto su la pagina DeepSeek: contesto da 380K, fino a 32.768 token di output, un unico prezzo a qualsiasi ora.
Un modo rapido per percepire le proporzioni:
Verifica rapida
Quale parte della richiesta limita max_tokens=200?
Mostra risposta
Risposta: La risposta
max_tokens limita solo la risposta; il prompt viene fatturato così com'è, quindi riducilo separatamente.
Vuoi 100.000 richieste al mese? Moltiplica per 100: 40.000.000 di input e 6.000.000 di token di output. Inserisci quei due numeri in ::cost allo stesso modo e leggi la riga.
Verificalo sulle tue richieste#
Il 79,4% della card non dice nulla sul tuo prompt, sul tuo OS o sulle parole dei tuoi utenti. Scrivi 20 richieste reali con i comandi che accetteresti e confronta, esattamente come in testare un prompt su un piccolo set di eval. Includine alcune che lo script dovrebbe rifiutare o segnalare come incerte, come "cancella tutto" o "rendi più veloce il mio computer".
Quando qualcosa va storto, il sintomo di solito indica la soluzione:
Could not read the reply as JSON: stampa una volta il testo grezzo. Un prefisso come "Sure!" viene gestito; una risposta tagliata dal limite no, quindi controllafinish_reason.401o404: chiave, base URL o id del modello sbagliati. L'elenco è in API 401 o 404: chiave, base URL, id del modello.- Comandi adatti a un sistema diverso: la causa è la riga di contesto. Mantienila accurata (OS, package manager, strumenti); costa qualche decina di token e risparmia un
aptsbagliato su una macchina Fedora.
Stesso compito, modello diverso? Sostituisci MODEL con l'id GLM o MiniMax dai documenti, mantieni il prompt e confronta i conteggi di token che lo script stampa con la tabella qui sopra.



