블로그로 돌아가기

비용 최적화가이드

LLM API로 자연어를 셸 명령으로: JSON과 비용

저렴한 모델에게 셸 명령 하나를 JSON으로 요청하고, 거부하거나 불확실한 요청을 표시하며, 아무것도 실행하지 않고, 1,000건 요청의 가격을 계산하는 Python 스크립트입니다.

작성자: CheapTokens Team 게시됨읽는 시간 7분

평범한 영어 요청이 command, refuse, unsure 유형을 가진 JSON 객체의 셸 명령 하나로 바뀌는 모습
이 페이지의 내용
  1. 좁은 작업: 답변이 짧은 이유
  2. 답변 형식: 명령, 거부 또는 불확실
  3. 스크립트
  4. 1,000건 요청 비용
  5. 자신의 요청으로 확인하세요

모델에게 세 개의 필드(type, command, explanation)를 가진 JSON 객체 하나를 요청하고, 답변을 수백 토큰으로 제한하며, 명령을 실행하지 않고 출력하세요. 토큰 기준으로 이런 요청은 대부분 프롬프트이고 답변은 짧고 예측 가능합니다. 아래 가정대로라면 1,000건의 비용은 DeepSeek-V4-Flash에서 $0.012입니다.

내부 CLI에 "원하는 것을 설명하세요" 입력란을 추가한다고 가정해 봅시다. 누군가 내 홈 폴더에서 100 MB보다 큰 파일 찾기라고 입력하면, 읽고 확인하고 붙여넣을 수 있는 한 줄을 돌려받고 싶을 것입니다. 여기서 두 가지 질문이 나옵니다. 모델이 추측하거나 위험한 일을 하지 않게 하려면 어떻게 해야 할까요? 그리고 하루 종일 100명이 사용하면 비용은 얼마일까요?

좁은 작업: 답변이 짧은 이유#

2026년 11월 9일, 한 Telegram 채널이 정확히 이 한 가지 작업만 수행하는 오픈 모델 dale-0.8b의 모델 카드를 다시 소개했습니다. Hugging Face의 모델 카드(2026년 11월 10일 확인)에 따르면, dale-0.8b는 Apache-2.0 라이선스의 Qwen3.5-0.8B를 완전 파인튜닝한 모델로, 스페인어 또는 영어 요청을 하나의 Linux bash 명령으로 바꿉니다. 저자 Manuel García는 자체 테스트 세트에서 79.4%(471건 중 374건)를 보고했으며, 이는 파인튜닝하지 않은 Qwen3.5-9B 베이스와 Gemma 4 E4B-it의 62.8%와 비교됩니다. 이는 한 사람의 테스트 세트에서 자기 보고한 수치이므로 순위가 아니라 하나의 데이터 포인트로 받아들이세요.

우리에게 유용한 것은 모델이 아니라 작업의 형태입니다. 카드에는 고정 시스템 프롬프트, 컨텍스트 줄(OS, 배포판, 패키지 관리자, 도구)과 요청, 사고 비활성화, 200 토큰으로 제한된 답변이 설명되어 있습니다. 이 제한은 비용을 조절하는 두 가지 레버 중 하나입니다. 답변이 짧은 JSON 객체 하나일 때 제한이 이를 제한하므로 출력 쪽은 짧고 예측 가능합니다. 다른 레버는 프롬프트입니다. 각 쪽이 청구서에서 차지하는 비중은 아래 표에 나와 있으며, 모델마다 다릅니다.

이를 위해 0.8B 모델이 필요하지는 않습니다. 같은 형식이 모든 API 모델에서 작동하며, 이 글의 나머지 부분에서 그것을 구축합니다.

하나의 요청 경로: 프롬프트 입력, JSON 출력, 사람이 결정
스크립트의 요청 흐름: 직접 실행하기 전까지는 아무것도 실행되지 않습니다.

답변 형식: 명령, 거부 또는 불확실#

핵심은 모델이 항상 "아니오"라고 말할 합법적인 방법을 가진다는 것입니다. 답변은 하나의 객체입니다:

json
{"type": "command | refuse | unsure", "command": "...", "explanation": "..."}
  • command: 하나의 bash 명령과 그것이 하는 일에 대한 한 문장 설명.
  • refuse: 요청이 파괴적이거나 셸과 관련이 없습니다. 명령 없음.
  • unsure: 요청이 너무 모호합니다("내 디스크 정리해 줘"). 설명에 명확히 하는 질문이 들어 있습니다.

unsure이 없으면 모호한 요청은 추측을 강요하고, 추측된 rm은 기억에 남는 바로 그 명령입니다. 카드의 형식에도 같은 세 가지 유형이 있습니다. 우리는 모델이 아니라 아이디어를 재사용합니다.

짝 맞추기

각 답변 유형을 스크립트가 해야 할 일과 연결하세요

  • command명령과 설명을 표시하고, 사용자가 직접 복사해서 실행합니다
  • refuse설명을 출력하고 실행할 것은 아무것도 제공하지 않습니다
  • unsure질문을 출력하고 사용자가 다시 표현하게 합니다

스크립트#

파일 하나, openai 패키지, 환경 변수에 키. 기본 URL과 전체 모델 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, 카드의 제한과 동일. JSON 객체 하나는 쉽게 들어가고, 장황한 답변은 비용을 늘리며 계속되는 대신 200 토큰에서 잘립니다. 제한은 전체 답변에 적용되므로, 모델이 추론을 길게 쓰면 그 한도를 잡아먹을 수 있습니다. 답변이 finish_reason가 length인 채로 끝난다면 제한을 조금 높이세요. 자세한 내용은 max_tokens로 출력 토큰 줄이기를 참고하세요.
  • 형식은 프롬프트에서 요청한 다음 방어적으로 파싱합니다. raw_decode은 { 앞의 잘못된 코드 펜스나 문장을 건너뜁니다. 더 나아가려면 OpenAI 호환 API에서 유효한 JSON과 답변의 json.JSONDecodeError 해결 방법을 참고하세요.

무엇이든 실행하기 전에 단계 순서가 명확한지 확인하세요:

순서대로 배열하기

스크립트 단계를 올바른 순서로 배치하세요

  1. 프롬프트 구성: 고정 시스템 텍스트, 컨텍스트 줄, 요청
  2. 작은 max_tokens로 API 호출
  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개 토큰입니다. 100만 토큰당 가격은 현재 기준이며 당사 요금제에 따라 함께 변경됩니다. 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%는 당신의 프롬프트, OS, 사용자의 표현에 대해 아무것도 말해주지 않습니다. 작은 평가 세트에서 프롬프트 테스트하기에서처럼 수용할 명령과 함께 실제 요청 20개를 작성하고 비교하세요. "모든 것을 삭제해" 또는 "내 컴퓨터를 더 빠르게 해줘"처럼 스크립트가 거부하거나 불확실로 표시해야 하는 몇 가지를 포함하세요.

문제가 생기면 증상이 보통 해결책을 가리킵니다:

  • Could not read the reply as JSON: 원시 텍스트를 한 번 출력하세요. "Sure!" 같은 접두사는 처리되지만, 제한으로 잘린 답변은 처리되지 않으므로 finish_reason을 확인하세요.
  • 401 또는 404: 잘못된 키, 기본 URL 또는 모델 id. 목록은 API 401 또는 404: 키, 기본 URL, 모델 id에 있습니다.
  • 다른 시스템에 맞는 명령: 컨텍스트 줄이 원인입니다. 정확하게 유지하세요(OS, 패키지 관리자, 도구). 수십 토큰이 들고 Fedora 박스에서 잘못된 apt을 막아줍니다.

같은 작업, 다른 모델? MODEL을 문서의 GLM 또는 MiniMax id로 바꾸고, 프롬프트를 유지하며, 스크립트가 출력하는 토큰 수를 위 표와 비교하세요.