KostenoptimierungAnleitung
Natürliche Sprache zu Shell-Befehl mit einer LLM-API: JSON und Kosten
Ein Python-Skript, das ein günstiges Modell um einen Shell-Befehl als JSON bittet, Anfragen ablehnt oder als unsicher markiert, nie etwas ausführt und 1.000 Anfragen bepreist.

Auf dieser Seite
Bitte das Modell um ein einziges JSON-Objekt mit drei Feldern (type, command, explanation), begrenze die Antwort auf ein paar hundert Tokens und gib den Befehl aus, statt ihn auszuführen. In Tokens gerechnet besteht eine solche Anfrage überwiegend aus dem Prompt mit einer kurzen, vorhersehbaren Antwort: Unter den unten genannten Annahmen kosten 1.000 davon $0.012 bei DeepSeek-V4-Flash.
Angenommen, du fügst einem internen CLI eine Box „beschreibe, was du willst“ hinzu. Jemand tippt finde Dateien größer als 100 MB in meinem Home-Ordner, und du willst eine Zeile zurück, die man lesen, prüfen und einfügen kann. Daraus folgen zwei Fragen: Wie hältst du das Modell davon ab zu raten oder etwas Gefährliches zu tun, und was kostet das, wenn hundert Leute es den ganzen Tag nutzen?
Eine eng umrissene Aufgabe: warum die Antwort kurz ist#
Am 9. November 2026 hat ein Telegram-Kanal die Model Card von dale-0.8b nacherzählt, einem offenen Modell, das genau diese eine Aufgabe erledigt. Laut der Model Card auf Hugging Face (geprüft am 10. November 2026) ist dale-0.8b ein vollständiges Fine-Tuning von Qwen3.5-0.8B unter Apache-2.0, das eine spanische oder englische Anfrage in einen einzigen Linux-Bash-Befehl verwandelt. Der Autor, Manuel García, meldet 79,4 % (374 von 471 Fällen) auf seinem eigenen Testset, gegenüber 62,8 % für Qwen3.5-9B base und für Gemma 4 E4B-it ohne Fine-Tuning. Das sind selbst gemeldete Zahlen auf dem Testset einer einzelnen Person, also behandle sie als Datenpunkt, nicht als Ranking.
Für uns ist die Form der Aufgabe nützlich, nicht das Modell. Die Card beschreibt einen festen System-Prompt, eine Kontextzeile (OS, Distro, Paketmanager, Tools) plus die Anfrage, abgeschaltetes Thinking und eine Antwort, die auf 200 Tokens begrenzt ist. Diese Grenze ist einer der beiden Hebel für die Kosten: Wenn die Antwort ein kurzes JSON-Objekt ist, ist die Ausgabeseite kurz und vorhersehbar, weil die Grenze sie begrenzt. Der andere Hebel ist der Prompt. Welchen Anteil jede Seite an der Rechnung hat, zeigt die Tabelle unten; er unterscheidet sich von Modell zu Modell.
Dafür brauchst du kein 0.8B-Modell. Dasselbe Format funktioniert mit jedem API-Modell, und genau das baut der Rest dieses Artikels.

Das Antwortformat: Befehl, Ablehnung oder Unsicherheit#
Der Trick ist, dass das Modell immer einen legitimen Weg hat, „nein“ zu sagen. Die Antwort ist ein Objekt:
{"type": "command | refuse | unsure", "command": "...", "explanation": "..."}command: ein Bash-Befehl plus eine einzeilige Erklärung, was er tut.refuse: die Anfrage ist destruktiv oder hat nichts mit der Shell zu tun. Kein Befehl.unsure: die Anfrage ist zu vage („räum meine Festplatte auf“). Die Erklärung enthält eine klärende Frage.
Ohne unsure erzwingt eine vage Anfrage eine Vermutung, und eine geratene rm ist die, an die du dich erinnerst. Das Format der Card hat dieselben drei Typen; wir übernehmen die Idee, nicht das Modell.
Paare zuordnen
Ordne jeden Antworttyp dem zu, was das Skript tun soll
Tippe auf ein Element links, dann auf sein Paar
- commandBefehl und Erklärung anzeigen; die Person kopiert und führt ihn selbst aus
- refuseDie Erklärung ausgeben und nichts zum Ausführen anbieten
- unsureDie Frage ausgeben und den Nutzer neu formulieren lassen
Das Skript#
Eine Datei, das openai-Paket, der Schlüssel in einer Umgebungsvariable. Die Basis-URL und die vollständige Modell-ID stammen aus der CheapTokens-Doku; kurze IDs werden nicht akzeptiert, also verwende 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')Führe es als python shellhelp. aus. Drei Entscheidungen sind hier wichtig:
- Nirgends
subprocess. Der Befehl ist Text auf dem Bildschirm. Ihn auszuführen ist deine Entscheidung, nachdem du ihn gelesen hast. max_tokens=200, wie die Grenze der Card. Ein JSON-Objekt passt leicht hinein; eine ausschweifende Antwort wird bei 200 Tokens abgeschnitten, statt auf deine Kosten weiterzulaufen. Die Grenze gilt für die gesamte Antwort, also kann ein Modell, das seine Begründung ausschreibt, das Limit auffressen. Endet eine Antwort jemals mitfinish_reasongleichlength, erhöhe die Grenze ein wenig. Mehr dazu in Ausgabe-Tokens mit max_tokens kürzen.- Das Format wird im Prompt verlangt und dann defensiv geparst.
raw_decodeüberspringt einen verirrten Code-Fence oder einen Satz vor dem{. Wenn du weiter gehen willst, siehe valides JSON von einer OpenAI-kompatiblen API und die Lösung für json.JSONDecodeError bei einer Antwort.
Bevor du irgendetwas ausführst, prüfe, ob die Reihenfolge der Schritte klar ist:
In die richtige Reihenfolge bringen
Bringe die Schritte des Skripts in die richtige Reihenfolge
Ziehe die Elemente oder benutze die Pfeile
- Den Prompt bauen: fester Systemtext, eine Kontextzeile, die Anfrage
- Die API mit einem kleinen max_tokens aufrufen
- Das JSON parsen und prüfen, dass type einer der drei ist
- Den Befehl anzeigen und warten, dass die Person ihn ausführt
Was 1.000 Anfragen kosten#
Die letzte Zeile des Skripts gibt usage aus. Das ist die echte Zahl, und du solltest deine eigene lesen. Bis du sie hast, arbeite mit einer Annahme: Der Prompt kommt auf etwa 400 Input-Tokens (der reine Prompt im Skript sind grob gezählt etwa 150 Tokens; der Rest ist Platz für die drei oder vier Beispiel-Paare aus Anfrage und Antwort, die du wahrscheinlich ergänzt), und die JSON-Antwort auf etwa 60 Output-Tokens.
Für 1.000 Anfragen sind das 400 × 1.000 = 400.000 Input-Tokens und 60 × 1.000 = 60.000 Output-Tokens. Die Tabelle bepreist genau das für jedes Modell im Katalog, mit der OpenRouter-Spalte daneben; die Zahlen stammen aus Live-Preisen, bleiben also aktuell.
| Modell | Input $/1M | Output $/1M | Pro Anfrage | 1.000 Anfragen | OpenRouter, 1.000 Anfragen |
|---|---|---|---|---|---|
| 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 Input- und 60 Output-Tokens pro Anfrage. Die Preise pro 1 Mio. Tokens sind aktuell und ändern sich zusammen mit unseren Tarifen; die OpenRouter-Spalte zeigt Referenzpreise für dieselbe Arbeitslast.
Insgesamt, Input und Output zusammengerechnet, ist DeepSeek-V4-Flash für diese Arbeitslast günstiger als OpenRouter. In Tokens gerechnet besteht die Anfrage fast nur aus Input und die Antwort ist kurz, aber beide Seiten werden abgerechnet, also kürze den Prompt und die Antwort gleichermaßen. Die Preise pro Modell stehen auf der Preisseite, und das Modell selbst wird auf der DeepSeek-Seite beschrieben: 380K Kontext, bis zu 32.768 Output-Tokens, ein Preis zu jeder Stunde.
Ein schneller Weg, die Proportionen zu spüren:
Kurzer Check
Welchen Teil der Anfrage begrenzt max_tokens=200?
Antwort anzeigen
Antwort: Die Antwort
max_tokens begrenzt nur die Antwort; der Prompt wird so abgerechnet, wie er ist, also kürze ihn separat.
Willst du 100.000 Anfragen im Monat? Multipliziere mit 100: 40.000.000 Input- und 6.000.000 Output-Tokens. Setze diese beiden Zahlen genauso in ::cost ein und lies die Zeile.
Prüfe es an deinen eigenen Anfragen#
Die 79,4 % der Card sagen nichts über deinen Prompt, dein OS oder die Formulierungen deiner Nutzer aus. Schreibe 20 echte Anfragen mit den Befehlen, die du akzeptieren würdest, und vergleiche, genau wie in einen Prompt auf einem kleinen Eval-Set testen. Nimm ein paar auf, die das Skript ablehnen oder als unsicher markieren sollte, etwa „alles löschen“ oder „mach meinen Computer schneller“.
Wenn etwas schiefgeht, weist das Symptom meist auf die Lösung hin:
Could not read the reply as JSON: gib den Rohtext einmal aus. Ein Präfix wie „Sure!“ wird behandelt; eine durch die Grenze abgeschnittene Antwort nicht, also prüfefinish_reason.401oder404: falscher Schlüssel, falsche Basis-URL oder falsche Modell-ID. Die Liste steht in API 401 oder 404: Schlüssel, Basis-URL, Modell-ID.- Befehle, die zu einem anderen System passen: die Kontextzeile ist die Ursache. Halte sie korrekt (OS, Paketmanager, Tools); sie kostet ein paar Dutzend Tokens und erspart ein falsches
aptauf einer Fedora-Kiste.
Gleiche Aufgabe, anderes Modell? Tausche MODEL gegen die GLM- oder MiniMax-ID aus der Doku, behalte den Prompt und vergleiche die Token-Zahlen, die das Skript ausgibt, mit der Tabelle oben.



