Tối ưu chi phíHướng dẫn
Chuyển ngôn ngữ tự nhiên thành lệnh shell với API LLM: JSON và chi phí
Một script Python yêu cầu một model rẻ trả về một lệnh shell dưới dạng JSON, từ chối hoặc đánh dấu các yêu cầu mơ hồ, không bao giờ chạy gì, và tính giá cho 1.000 yêu cầu.

Trên trang này
Yêu cầu model trả về một đối tượng JSON với ba trường (type, command, explanation), giới hạn câu trả lời ở vài trăm token, và in ra lệnh thay vì chạy nó. Tính theo token, một yêu cầu như vậy chủ yếu là prompt với câu trả lời ngắn và dễ đoán: theo các giả định bên dưới, 1.000 yêu cầu như thế tốn $0.012 trên DeepSeek-V4-Flash.
Giả sử bạn đang thêm một ô "mô tả điều bạn muốn" vào một CLI nội bộ. Ai đó gõ tìm các tệp lớn hơn 100 MB trong thư mục home của tôi, và bạn muốn nhận lại một dòng họ có thể đọc, kiểm tra và dán. Hai câu hỏi tiếp theo: làm sao để model không đoán bừa hay làm điều gì nguy hiểm, và việc này tốn bao nhiêu khi một trăm người dùng cả ngày?
Một tác vụ hẹp: vì sao câu trả lời ngắn#
Ngày 9 tháng 11 năm 2026, một kênh Telegram kể lại model card của dale-0.8b, một model mở làm đúng một việc này. Theo model card trên Hugging Face (kiểm tra ngày 10 tháng 11 năm 2026), dale-0.8b là một bản fine-tune toàn phần của Qwen3.5-0.8B theo Apache-2.0, biến một yêu cầu tiếng Tây Ban Nha hoặc tiếng Anh thành một lệnh bash Linux. Tác giả, Manuel García, báo cáo 79,4% (374 trên 471 trường hợp) trên bộ test của riêng anh ấy, so với 62,8% của Qwen3.5-9B base và của Gemma 4 E4B-it không fine-tune. Đó là các con số tự báo cáo trên bộ test của một người, nên hãy coi chúng như một điểm dữ liệu, không phải một bảng xếp hạng.
Điều hữu ích với chúng ta là hình dạng của tác vụ, không phải model. Model card mô tả một system prompt cố định, một dòng ngữ cảnh (OS, distro, trình quản lý gói, công cụ) cộng với yêu cầu, tắt thinking, và câu trả lời giới hạn ở 200 token. Giới hạn đó là một trong hai đòn bẩy về chi phí: khi câu trả lời là một đối tượng JSON ngắn, phía output ngắn và dễ đoán vì giới hạn chặn nó lại. Đòn bẩy còn lại là prompt. Mỗi bên chiếm bao nhiêu phần trong hóa đơn, bảng bên dưới cho thấy; nó khác nhau tùy model.
Bạn không cần model 0.8B cho việc đó. Cùng định dạng này hoạt động trên bất kỳ model API nào, và đó là thứ phần còn lại của bài viết xây dựng.

Định dạng trả lời: command, refuse hoặc unsure#
Mẹo là model luôn có một cách hợp lệ để nói "không". Câu trả lời là một đối tượng:
{"type": "command | refuse | unsure", "command": "...", "explanation": "..."}command: một lệnh bash, kèm một câu giải thích lệnh làm gì.refuse: yêu cầu mang tính phá hoại hoặc không liên quan gì đến shell. Không có lệnh.unsure: yêu cầu quá mơ hồ ("dọn dẹp ổ đĩa của tôi"). Phần giải thích chứa một câu hỏi làm rõ.
Không có unsure, một yêu cầu mơ hồ buộc model phải đoán, và một rm đoán bừa là thứ bạn sẽ nhớ mãi. Định dạng của model card cũng có ba loại này; chúng ta tái dùng ý tưởng, không phải model.
Ghép cặp
Ghép mỗi loại trả lời với việc script nên làm
Nhấn vào một mục bên trái, rồi đến cặp của nó
- commandHiển thị lệnh và phần giải thích; người dùng tự sao chép và chạy
- refuseIn phần giải thích và không đưa ra gì để chạy
- unsureIn câu hỏi và để người dùng diễn đạt lại
Script#
Một tệp, gói openai, khóa trong một biến môi trường. Base URL và id model đầy đủ lấy từ tài liệu CheapTokens; id rút gọn không được chấp nhận, nên hãy dùng 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')Chạy nó bằng python shellhelp.. Ba lựa chọn quan trọng ở đây:
- Không có
subprocessở bất kỳ đâu. Lệnh chỉ là văn bản trên màn hình. Chạy nó là quyết định của bạn, sau khi bạn đọc nó. max_tokens=200, giống giới hạn của model card. Một đối tượng JSON vừa vặn dễ dàng; một câu trả lời lan man bị cắt ở 200 token thay vì chạy dài tốn tiền của bạn. Giới hạn áp dụng cho toàn bộ câu trả lời, nên nếu model viết ra lý luận của nó, phần đó có thể ăn hết hạn mức. Nếu có lúc câu trả lời kết thúc vớifinish_reasonbằnglength, hãy nâng giới hạn lên một chút. Thêm về điều này trong cắt token output bằng max_tokens.- Định dạng được yêu cầu trong prompt, rồi được parse phòng thủ.
raw_decodebỏ qua một code fence lạc hay một câu trước{. Nếu muốn đi xa hơn, xem JSON hợp lệ từ một API tương thích OpenAI và cách sửa lỗi json.JSONDecodeError trên một câu trả lời.
Trước khi chạy bất cứ thứ gì, hãy kiểm tra rằng thứ tự các bước đã rõ:
Sắp xếp thứ tự
Xếp các bước của script đúng thứ tự
Kéo các mục hoặc dùng mũi tên
- Dựng prompt: văn bản system cố định, một dòng ngữ cảnh, yêu cầu
- Gọi API với max_tokens nhỏ
- Parse JSON và kiểm tra rằng type là một trong ba loại
- Hiển thị lệnh và chờ người dùng chạy nó
1.000 yêu cầu tốn bao nhiêu#
Dòng cuối của script in ra usage. Đó là con số thật, và bạn nên đọc con số của chính mình. Cho đến khi có nó, hãy làm việc với một giả định: prompt vào khoảng 400 token input (prompt trần trong script ước chừng khoảng 150 token; phần còn lại là chỗ cho ba hoặc bốn cặp yêu cầu-và-trả lời mẫu mà bạn có thể sẽ thêm vào), và câu trả lời JSON khoảng 60 token output.
Với 1.000 yêu cầu, đó là 400 × 1.000 = 400.000 token input và 60 × 1.000 = 60.000 token output. Bảng định giá đúng như vậy cho mọi model trong danh mục, với cột OpenRouter bên cạnh; các con số lấy từ giá trực tiếp nên luôn cập nhật.
| Mô hình | Đầu vào $/1M | Đầu ra $/1M | Mỗi yêu cầu | 1.000 yêu cầu | OpenRouter, 1.000 yêu cầu |
|---|---|---|---|---|---|
| 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 đầu vào và 60 token đầu ra mỗi yêu cầu. Giá trên 1 triệu token là giá hiện hành và thay đổi cùng với biểu phí của chúng tôi; cột OpenRouter hiển thị giá tham chiếu cho cùng khối lượng công việc.
Tổng cộng, tính cả input và output, DeepSeek-V4-Flash rẻ hơn OpenRouter cho khối lượng công việc này. Tính theo token, yêu cầu gần như toàn là input và câu trả lời ngắn, nhưng cả hai phía đều bị tính tiền, nên hãy cắt gọn cả prompt lẫn câu trả lời. Giá từng model nằm trên trang định giá, và bản thân model được mô tả trên trang DeepSeek: ngữ cảnh 380K, tối đa 32.768 token output, một mức giá bất kể giờ nào.
Một cách nhanh để cảm nhận tỷ lệ:
Kiểm tra nhanh
max_tokens=200 giới hạn phần nào của yêu cầu?
Hiện đáp án
Trả lời: Câu trả lời
max_tokens chỉ giới hạn câu trả lời; prompt bị tính tiền nguyên như nó vốn có, nên hãy cắt gọn nó riêng.
Muốn 100.000 yêu cầu một tháng? Nhân với 100: 40.000.000 input và 6.000.000 token output. Đưa hai con số đó vào ::cost theo cùng cách và đọc dòng tương ứng.
Kiểm tra trên chính yêu cầu của bạn#
Con số 79,4% của model card không nói gì về prompt của bạn, OS của bạn hay cách người dùng của bạn diễn đạt. Hãy viết 20 yêu cầu thật cùng các lệnh bạn sẽ chấp nhận và so sánh, đúng như trong kiểm thử một prompt trên một bộ eval nhỏ. Bao gồm vài yêu cầu mà script nên từ chối hoặc đánh dấu unsure, chẳng hạn "xóa mọi thứ" hay "làm máy tính của tôi nhanh hơn".
Khi có sự cố, triệu chứng thường chỉ ra cách sửa:
Could not read the reply as JSON: in văn bản thô một lần. Một tiền tố như "Sure!" được xử lý; câu trả lời bị cắt bởi giới hạn thì không, nên hãy kiểm trafinish_reason.401hoặc404: sai khóa, base URL hoặc id model. Danh sách nằm trong API 401 hoặc 404: khóa, base URL, id model.- Các lệnh khớp với một hệ thống khác: dòng ngữ cảnh là nguyên nhân. Giữ nó chính xác (OS, trình quản lý gói, công cụ); nó tốn vài chục token và tránh một
aptsai trên máy Fedora.
Cùng tác vụ, model khác? Đổi MODEL sang id GLM hoặc MiniMax từ tài liệu, giữ nguyên prompt, và so sánh số token script in ra với bảng bên trên.



