กลับไปที่บล็อก

การลดต้นทุนคู่มือ

ภาษาธรรมชาติเป็นคำสั่งเชลล์ด้วย LLM API: JSON และค่าใช้จ่าย

สคริปต์ Python ที่ขอคำสั่งเชลล์หนึ่งคำสั่งจากโมเดลราคาถูกในรูปแบบ JSON ปฏิเสธหรือตั้งค่าสถานะคำขอที่ไม่ชัดเจน ไม่รันอะไรเลย และคิดราคา 1,000 คำขอ

โดย CheapTokens Teamเผยแพร่เมื่อ ใช้เวลาอ่าน 7 นาที

คำขอในภาษาอังกฤษธรรมดาเปลี่ยนเป็นคำสั่งเชลล์หนึ่งคำสั่งในออบเจกต์ JSON ที่มีประเภท command, refuse และ unsure
ในหน้านี้
  1. งานที่แคบ: ทำไมคำตอบจึงสั้น
  2. รูปแบบคำตอบ: command, refuse หรือ unsure
  3. สคริปต์
  4. ค่าใช้จ่ายของ 1,000 คำขอ
  5. ตรวจสอบกับคำขอของคุณเอง

ขอให้โมเดลคืนค่าเป็นออบเจกต์ JSON หนึ่งชิ้นที่มีสามฟิลด์ (type, command, explanation) จำกัดคำตอบไว้ที่สองสามร้อยโทเคน และพิมพ์คำสั่งออกมาแทนที่จะรันมัน ในแง่ของโทเคน คำขอแบบนี้ส่วนใหญ่เป็นพรอมป์ต์พร้อมคำตอบสั้นที่คาดเดาได้ ภายใต้สมมติฐานด้านล่าง 1,000 โทเคนมีค่าใช้จ่าย $0.012 บน DeepSeek-V4-Flash

สมมติว่าคุณกำลังเพิ่มกล่อง "อธิบายสิ่งที่คุณต้องการ" ให้กับ CLI ภายในองค์กร มีคนพิมพ์ find files bigger than 100 MB in my home folder และคุณต้องการได้กลับมาหนึ่งบรรทัดที่อ่านได้ ตรวจสอบได้ และวางได้ คำถามสองข้อตามมา: คุณจะกันไม่ให้โมเดลเดาสุ่มหรือทำอะไรอันตรายได้อย่างไร และค่าใช้จ่ายเป็นเท่าไรเมื่อมีคนร้อยคนใช้มันทั้งวัน?

งานที่แคบ: ทำไมคำตอบจึงสั้น#

เมื่อวันที่ 9 พฤศจิกายน 2026 ช่อง Telegram แห่งหนึ่งได้เล่าเรื่อง model card ของ dale-0.8b โมเดลเปิดที่ทำงานนี้เพียงอย่างเดียว ตาม model card บน 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 base และ Gemma 4 E4B-it ที่ไม่ได้ fine-tune ตัวเลขเหล่านี้เป็นการรายงานตนเองบนชุดทดสอบของคนคนเดียว จึงควรมองเป็นข้อมูลจุดหนึ่ง ไม่ใช่การจัดอันดับ

สิ่งที่มีประโยชน์สำหรับเราคือรูปร่างของงาน ไม่ใช่ตัวโมเดล model card อธิบายถึง system prompt แบบคงที่ บรรทัดบริบท (OS, distro, package manager, tools) บวกกับคำขอ ปิดการคิด และจำกัดคำตอบไว้ที่ 200 โทเคน เพดานนี้เป็นหนึ่งในสองคันโยกด้านค่าใช้จ่าย: เมื่อคำตอบเป็นออบเจกต์ JSON สั้น ๆ ฝั่งเอาต์พุตก็สั้นและคาดเดาได้ เพราะเพดานจำกัดมันไว้ คันโยกอีกตัวคือพรอมป์ต์ แต่ละฝั่งคิดเป็นสัดส่วนเท่าไรของบิล ตารางด้านล่างแสดงให้เห็น ซึ่งแตกต่างกันไปตามโมเดล

คุณไม่จำเป็นต้องใช้โมเดล 0.8B สำหรับสิ่งนี้ รูปแบบเดียวกันใช้ได้กับโมเดล API ใดก็ได้ และนั่นคือสิ่งที่บทความที่เหลือนี้สร้างขึ้น

เส้นทางของคำขอหนึ่งครั้ง: พรอมป์ต์เข้า JSON ออก มนุษย์เป็นผู้ตัดสินใจ
ลำดับการทำงานของคำขอในสคริปต์: ไม่มีอะไรถูกเรียกใช้จนกว่าคุณจะทำเอง

รูปแบบคำตอบ: command, refuse หรือ unsure#

เคล็ดลับคือโมเดลมีทางถูกต้องตามกฎหมายในการพูดว่า "ไม่" เสมอ คำตอบคือออบเจกต์หนึ่งชิ้น:

json
{"type": "command | refuse | unsure", "command": "...", "explanation": "..."}
  • command: คำสั่ง bash หนึ่งคำสั่ง พร้อมคำอธิบายหนึ่งประโยคว่ามันทำอะไร
  • refuse: คำขอเป็นอันตรายหรือไม่เกี่ยวกับเชลล์เลย ไม่มีคำสั่ง
  • unsure: คำขอกำกวมเกินไป ("clean up my disk") คำอธิบายมีคำถามเพื่อขอความชัดเจน

หากไม่มี unsure คำขอกำกวมจะบังคับให้เดา และ rm ที่เดาขึ้นมาคือสิ่งที่คุณจะจดจำ รูปแบบของ model card มีสามประเภทเดียวกัน เราใช้แนวคิดซ้ำ ไม่ใช่ตัวโมเดล

จับคู่

จับคู่ประเภทคำตอบแต่ละแบบกับสิ่งที่สคริปต์ควรทำ

  • commandแสดงคำสั่งและคำอธิบาย ผู้ใช้คัดลอกและรันเอง
  • refuseพิมพ์คำอธิบายและไม่เสนออะไรให้รัน
  • unsureพิมพ์คำถามและให้ผู้ใช้พูดใหม่

สคริปต์#

ไฟล์เดียว แพ็กเกจ openai คีย์ในตัวแปรสภาพแวดล้อม base URL และ model id แบบเต็มมาจาก เอกสาร CheapTokens ไม่รับ id แบบสั้น ดังนั้นใช้ deepseek-ai/DeepSeek-V4-Flash-0731

python
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.py find files bigger than 100 MB in my home folder มีสามตัวเลือกที่สำคัญตรงนี้:

  • ไม่มี subprocess ที่ไหนเลย คำสั่งเป็นข้อความบนหน้าจอ การรันมันเป็นการตัดสินใจของคุณ หลังจากที่คุณอ่านมันแล้ว
  • max_tokens=200 เหมือนกับเพดานใน model card ออบเจกต์ JSON หนึ่งชิ้นพอดีได้ง่าย ๆ คำตอบที่ยืดเยื้อจะถูกตัดที่ 200 โทเคนแทนที่จะยาวต่อโดยเสียค่าใช้จ่ายของคุณ เพดานนี้ใช้กับคำตอบทั้งหมด ดังนั้นหากโมเดลเขียนเหตุผลของมันออกมา สิ่งนั้นอาจกินลิมิตได้ หากคำตอบลงท้ายด้วย finish_reason เท่ากับ length ให้เพิ่มเพดานขึ้นเล็กน้อย อ่านเพิ่มเติมได้ที่ การตัดโทเคนเอาต์พุตด้วย max_tokens
  • รูปแบบถูกขอในพรอมป์ต์ แล้วแยกวิเคราะห์อย่างระมัดระวัง raw_decode ข้าม code fence ที่หลงมา หรือประโยคก่อน { หากต้องการไปไกลกว่านั้น ดู JSON ที่ถูกต้องจาก API ที่เข้ากันได้กับ OpenAI และวิธีแก้ json.JSONDecodeError บนคำตอบ

ก่อนที่คุณจะรันอะไร ให้ตรวจสอบว่าลำดับขั้นตอนชัดเจน:

เรียงลำดับ

เรียงลำดับขั้นตอนของสคริปต์ให้ถูกต้อง

  1. สร้างพรอมป์ต์: ข้อความ system แบบคงที่ บรรทัดบริบท คำขอ
  2. เรียก API ด้วย max_tokens เล็ก ๆ
  3. แยกวิเคราะห์ JSON และตรวจสอบว่า type เป็นหนึ่งในสามค่า
  4. แสดงคำสั่งและรอให้ผู้ใช้รันเอง

ค่าใช้จ่ายของ 1,000 คำขอ#

บรรทัดสุดท้ายของสคริปต์พิมพ์ usage นั่นคือตัวเลขจริง และคุณควรอ่านค่าของคุณเอง จนกว่าคุณจะมีมัน ให้ใช้สมมติฐาน: พรอมป์ต์รวมประมาณ 400 โทเคนอินพุต (พรอมป์ต์เปล่าในสคริปต์ประมาณ 150 โทเคนจากการนับคร่าว ๆ ที่เหลือเป็นที่ว่างสำหรับคู่คำขอ-คำตอบตัวอย่างสามหรือสี่คู่ที่คุณน่าจะเพิ่ม) และคำตอบ JSON ประมาณ 60 โทเคนเอาต์พุต

สำหรับ 1,000 คำขอ นั่นคือ 400 × 1,000 = 400,000 โทเคนอินพุต และ 60 × 1,000 = 60,000 โทเคนเอาต์พุต ตารางคิดราคาตามนั้นพอดีสำหรับทุกโมเดลในแคตตาล็อก โดยมีคอลัมน์ OpenRouter อยู่ข้าง ๆ ตัวเลขมาจากราคาสด จึงเป็นปัจจุบันเสมอ

โมเดล ต่อคำขอ 1,000 คำขอ OpenRouter, 1,000 คำขอ
GLM-5.3-Flash $0.00001 $0.0104 $0.0184
DeepSeek-V4-Flash $0.000012 $0.012 $0.0364
MiniMax-M2.7 $0.000029 $0.029 $0.348

400 โทเค็นอินพุตและ 60 โทเค็นเอาต์พุตต่อคำขอ ราคาต่อ 1M โทเค็นเป็นราคาปัจจุบันและเปลี่ยนแปลงไปพร้อมกับอัตราค่าบริการของเรา คอลัมน์ 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% ของ model card ไม่ได้บอกอะไรเกี่ยวกับพรอมป์ต์ของคุณ ระบบปฏิบัติการของคุณ หรือถ้อยคำของผู้ใช้ เขียนคำขอจริง 20 รายการพร้อมคำสั่งที่คุณจะยอมรับและเปรียบเทียบ ตามที่อธิบายใน การทดสอบพรอมป์ต์บนชุด eval ขนาดเล็ก รวมคำขอที่สคริปต์ควรปฏิเสธหรือตั้งค่าสถานะเป็น unsure สักสองสามรายการ เช่น "delete everything" หรือ "make my computer faster"

เมื่อมีอะไรผิดพลาด อาการมักชี้ไปที่วิธีแก้:

  • Could not read the reply as JSON: พิมพ์ข้อความดิบหนึ่งครั้ง คำนำหน้าเช่น "Sure!" ถูกจัดการแล้ว แต่คำตอบที่ถูกตัดด้วยเพดานไม่ใช่ ดังนั้นตรวจสอบ finish_reason
  • 401 หรือ 404: คีย์ base URL หรือ model id ผิด รายการอยู่ใน API 401 หรือ 404: คีย์, base URL, model id
  • คำสั่งที่เข้ากับระบบอื่น: บรรทัดบริบทคือสาเหตุ ทำให้มันถูกต้อง (OS, package manager, tools) มันใช้ต้นทุนไม่กี่สิบโทเคนและช่วยประหยัด apt ที่ผิดบนเครื่อง Fedora

งานเดียวกัน แต่โมเดลต่างกัน? สลับ MODEL เป็น id ของ GLM หรือ MiniMax จากเอกสาร คงพรอมป์ต์ไว้ และเปรียบเทียบจำนวนโทเคนที่สคริปต์พิมพ์กับตารางด้านบน