Оптимізація витратПосібник
Природна мова в shell-команду через LLM API: JSON і вартість
Python-скрипт, який просить дешеву модель повернути одну shell-команду як JSON, відхиляє або позначає невпевнені запити, ніколи нічого не виконує й розраховує вартість 1 000 запитів.

На цій сторінці
Попросіть модель повернути один JSON-об'єкт із трьома полями (type, command, explanation), обмежте відповідь кількома сотнями токенів і надрукуйте команду замість того, щоб її виконувати. У перерахунку на токени такий запит — це переважно промпт із короткою, передбачуваною відповіддю: за наведених нижче припущень 1 000 таких запитів коштують $0.012 на DeepSeek-V4-Flash.
Уявіть, що ви додаєте до внутрішнього CLI поле «опишіть, що ви хочете». Хтось вводить знайди файли більші за 100 МБ у моїй домашній теці, і ви хочете отримати назад один рядок, який можна прочитати, перевірити й вставити. Звідси два питання: як не дати моделі вгадувати чи робити щось небезпечне, і скільки це коштує, коли сотня людей користується цим цілий день?
Вузька задача: чому відповідь коротка#
9 листопада 2026 року Telegram-канал переповів картку моделі dale-0.8b — відкритої моделі, яка робить саме цю одну роботу. Згідно з карткою моделі на Hugging Face (перевірено 10 листопада 2026 року), dale-0.8b — це повний fine-tune Qwen3.5-0.8B під Apache-2.0, який перетворює запит іспанською чи англійською на одну команду Linux bash. Автор, Manuel García, повідомляє про 79,4% (374 з 471 випадку) на власному тестовому наборі проти 62,8% для базової Qwen3.5-9B і для Gemma 4 E4B-it без fine-tuning. Це самостійно заявлені цифри на тестовому наборі однієї людини, тож ставтеся до них як до однієї точки даних, а не як до рейтингу.
Для нас корисна форма задачі, а не сама модель. Картка описує фіксований системний промпт, рядок контексту (ОС, дистрибутив, менеджер пакетів, інструменти) плюс запит, вимкнене мислення й відповідь, обмежену 200 токенами. Це обмеження — один із двох важелів впливу на вартість: коли відповідь — один короткий JSON-об'єкт, вихідна частина коротка й передбачувана, бо обмеження її стримує. Другий важіль — промпт. Яку частку рахунку становить кожна сторона, показує таблиця нижче; вона відрізняється від моделі до моделі.
Для цього вам не потрібна модель на 0.8B. Той самий формат працює на будь-якій API-моделі, і саме це будує решта статті.

Формат відповіді: команда, відмова або невпевненість#
Хитрість у тому, що модель завжди має законний спосіб сказати «ні». Відповідь — це один об'єкт:
{"type": "command | refuse | unsure", "command": "...", "explanation": "..."}command: одна команда bash плюс пояснення одним реченням, що вона робить.refuse: запит руйнівний або не стосується оболонки. Без команди.unsure: запит надто розпливчастий («почисти мій диск»). Пояснення містить уточнювальне питання.
Без unsure розпливчастий запит змушує вгадувати, а вгаданий rm — це те, що ви запам'ятаєте. Формат картки має ті самі три типи; ми перевикористовуємо ідею, а не модель.
З'єднайте пари
Зіставте кожен тип відповіді з тим, що має робити скрипт
Торкніться елемента ліворуч, потім його пари
- commandПоказати команду й пояснення; людина сама копіює й виконує її
- refuseНадрукувати пояснення й не пропонувати нічого для виконання
- unsureНадрукувати питання й дати користувачеві переформулювати
Скрипт#
Один файл, пакет openai, ключ у змінній середовища. Базовий URL і повний ідентифікатор моделі беруться з документації CheapTokens; короткі ідентифікатори не приймаються, тож використовуйте 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')Запускайте як python shellhelp.. Тут важливі три рішення:
- Жодного
subprocessніде. Команда — це текст на екрані. Виконати її — ваше рішення, після того як ви її прочитаєте. max_tokens=200, як і обмеження в картці. Один JSON-об'єкт легко вміщується; балакуча відповідь обрізається на 200 токенах, а не тягнеться далі за ваш рахунок. Обмеження застосовується до всієї відповіді, тож якщо модель виписує свої міркування, це може з'їсти ліміт. Якщо відповідь колись закінчується зfinish_reason, що дорівнюєlength, трохи підніміть обмеження. Докладніше про це в обрізанні вихідних токенів за допомогою max_tokens.- Формат задається в промпті, а потім розбирається оборонно.
raw_decodeпропускає зайвий блок коду чи речення перед{. Якщо хочете піти далі, дивіться валідний JSON від OpenAI-сумісного API і виправлення для json.JSONDecodeError у відповіді.
Перш ніж щось запускати, переконайтеся, що порядок кроків зрозумілий:
Розставте по порядку
Розставте кроки скрипта в правильному порядку
Перетягніть елементи або скористайтеся стрілками
- Побудувати промпт: фіксований системний текст, рядок контексту, запит
- Викликати API з малим max_tokens
- Розібрати JSON і перевірити, що type — один із трьох
- Показати команду й дочекатися, поки людина її виконає
Скільки коштують 1 000 запитів#
Останній рядок скрипта друкує usage. Це справжнє число, і вам варто прочитати своє власне. Поки його немає, працюйте з припущенням: промпт становить близько 400 вхідних токенів (голий промпт у скрипті — приблизно 150 токенів за грубим підрахунком; решта — запас для трьох-чотирьох прикладів пар запит-відповідь, які ви, ймовірно, додасте), а JSON-відповідь — близько 60 вихідних токенів.
Для 1 000 запитів це 400 × 1 000 = 400 000 вхідних токенів і 60 × 1 000 = 60 000 вихідних токенів. Таблиця розраховує ціну саме для цього для кожної моделі в каталозі, з колонкою OpenRouter поруч; цифри беруться з живих цін, тож залишаються актуальними.
| Модель | Вхід $/1М | Вихід $/1М | За запит | 1 000 запитів | OpenRouter, 1 000 запитів |
|---|---|---|---|---|---|
| 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 вхідних і 60 вихідних токенів на запит. Ціни за 1 млн токенів актуальні та змінюються разом із нашими тарифами; стовпець OpenRouter показує довідкові ціни для того самого навантаження.
Загалом, з урахуванням вхідних і вихідних токенів разом, для цього навантаження DeepSeek-V4-Flash виходить дешевше за OpenRouter. У перерахунку на токени запит — це майже весь вхід, а відповідь коротка, але оплачуються обидві сторони, тож урізайте і промпт, і відповідь. Ціни за моделями — на сторінці цін, а сама модель описана на сторінці DeepSeek: контекст 380K, до 32 768 вихідних токенів, одна ціна в будь-яку годину.
Швидкий спосіб відчути пропорції:
Швидка перевірка
Яку частину запиту обмежує max_tokens=200?
Показати відповідь
Відповідь: Відповідь
max_tokens обмежує лише відповідь; промпт оплачується як є, тож урізайте його окремо.
Хочете 100 000 запитів на місяць? Помножте на 100: 40 000 000 вхідних і 6 000 000 вихідних токенів. Вставте ці два числа в ::cost так само й прочитайте рядок.
Перевірте це на власних запитах#
79,4% з картки нічого не кажуть про ваш промпт, вашу ОС чи формулювання ваших користувачів. Напишіть 20 реальних запитів із командами, які ви б прийняли, і порівняйте — точно як у тестуванні промпту на невеликому наборі eval. Додайте кілька таких, які скрипт має відхилити або позначити як unsure, наприклад «видали все» чи «зроби мій комп'ютер швидшим».
Коли щось іде не так, симптом зазвичай вказує на виправлення:
Could not read the reply as JSON: один раз надрукуйте сирий текст. Префікс на кшталт «Sure!» обробляється; відповідь, обрізана обмеженням, — ні, тож перевіртеfinish_reason.401або404: неправильний ключ, базовий URL або ідентифікатор моделі. Перелік — у API 401 або 404: ключ, базовий URL, ідентифікатор моделі.- Команди, що підходять для іншої системи: причина — рядок контексту. Тримайте його точним (ОС, менеджер пакетів, інструменти); це коштує кілька десятків токенів і рятує від неправильного
aptна машині з Fedora.
Та сама задача, інша модель? Замініть MODEL на ідентифікатор GLM або MiniMax з документації, залиште промпт і порівняйте кількості токенів, які друкує скрипт, із таблицею вище.



