API & SDK가이드
Python에서 AI 에이전트 토큰 비용 추적: usage, 예산, 중단
모든 요청의 usage 필드를 기록하고, 작업별로 합산하며, 달러 예산에서 에이전트를 중단하세요. OpenAI 호환 API를 위한 작동하는 Python 예제입니다.

에이전트가 10분 동안 실행되었는데 청구서가 작업보다 큽니다. 이유를 확인하려면 모든 API 응답에서 usage 객체를 읽고, 작업별로 합산한 뒤 모델의 1M 토큰당 가격을 곱하세요. 이를 제한하려면 각 단계 후에 그 누적 합계를 예산과 비교하고 초과하면 루프를 중단하세요. 아래는 정확히 이 작업을 수행하는 작은 Python 클래스입니다.
이 예제는 OpenAI 호환 엔드포인트 https:/과 모델 ID deepseek-(380K 컨텍스트, 답변당 최대 32,768 출력 토큰)를 사용합니다. 실행을 시작하기 전에 추정하려는 것은 다른 작업이며, AI 에이전트 비용을 추정하는 방법에서 다룹니다. 이 글은 실행 중에 측정하고 제한하는 것에 관한 것입니다.
숫자가 있는 곳: usage 객체#
모든 비스트리밍 채팅 완료는 usage 객체와 함께 반환됩니다. DeepSeek의 API 레퍼런스(2026년 10월 26일 확인)에 나열된 필드는 다음과 같습니다:
prompt_tokens— 이번에 보낸 모든 것: 시스템 프롬프트, 히스토리, 도구 결과.completion_— 모델이 다시 작성한 것.tokens total_tokens— 둘을 더한 값.
DeepSeek는 선택적 추가 항목도 문서화합니다: prompt_과 prompt_(둘을 더하면 prompt_tokens), 그리고 사고 모델용 completion_. 이 페이지에는 게시 날짜가 없고, 이러한 추가 항목은 OpenAI 형식의 확장이므로 아래 코드는 이를 선택 사항으로 취급하고 처음 두 필드에만 의존합니다.
왜 마지막 단계만이 아니라 모든 단계를 기록해야 할까요? 에이전트는 매번 커지는 컨텍스트를 다시 보내기 때문입니다. 1단계는 2,000 입력 토큰을 보낼 수 있고, 30단계는 9,000을 보낼 수 있으며, 청구서는 이 모든 것의 합계입니다. 그 이유는 긴 채팅이 더 비싼 이유에 설명되어 있고, 입력/출력 분할은 입력 vs 출력 토큰에 있습니다.
에이전트를 중단시키는 미터#
전체는 이렇습니다: 토큰을 합산하고, 단계당 한 줄을 출력하며, 예산을 초과하면 오류를 발생시키는 미터와 그 주위의 최소한의 에이전트 루프입니다. SDK를 pip install openai로 설치하고 키를 CHEAPTOKENS_에 넣으세요.
import os
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'
# example prices, USD per 1M tokens: use the current ones from the pricing table
PRICE_IN = 0.10
PRICE_OUT = 0.40
class BudgetExceeded(Exception):
pass
class TaskMeter:
def __init__(self, budget_usd):
self.budget = budget_usd
self.steps = 0
self.tokens_in = 0
self.tokens_out = 0
self.reasoning = 0
@property
def cost(self):
return (self.tokens_in * PRICE_IN + self.tokens_out * PRICE_OUT) / 1_000_000
def add(self, usage):
self.steps += 1
self.tokens_in += usage.prompt_tokens
self.tokens_out += usage.completion_tokens
details = getattr(usage, 'completion_tokens_details', None)
self.reasoning += getattr(details, 'reasoning_tokens', 0) or 0
print(f'step {self.steps}: in={usage.prompt_tokens} '
f'out={usage.completion_tokens} task=${self.cost:.4f}')
if self.cost > self.budget:
raise BudgetExceeded(
f'budget ${self.budget:.2f} crossed at step {self.steps}: '
f'${self.cost:.4f}')
def run_task(goal, budget_usd=0.50, max_steps=40):
meter = TaskMeter(budget_usd)
messages = [
{'role': 'system', 'content': 'Work step by step. Reply DONE when finished.'},
{'role': 'user', 'content': goal},
]
for _ in range(max_steps):
resp = client.chat.completions.create(
model=MODEL, messages=messages, max_tokens=1024)
meter.add(resp.usage) # log first, then decide
reply = resp.choices[0].message.content or ''
messages.append({'role': 'assistant', 'content': reply})
if 'DONE' in reply:
break
messages.append({'role': 'user', 'content': 'Continue.'})
return meter
try:
meter = run_task('Summarise the main risks of a database migration.')
print(f'finished: {meter.steps} steps, ${meter.cost:.4f}')
except BudgetExceeded as err:
print('stopped:', err)그 안의 세 가지 선택은 따라 할 가치가 있습니다.
결정하기 전에 기록하세요. meter.add은 먼저 단계를 기록한 다음에야 예산을 확인하므로, 중단된 실행도 완전한 추적을 남깁니다. 확인은 선을 넘은 호출 이후에 발생하므로 최대 한 단계까지 초과할 수 있습니다. max_tokens=1024은 그 마지막 단계의 출력을 제한합니다.
하나가 아닌 두 개의 하드 스톱. max_steps은 많이 쓰지 않고 루프하는 에이전트를 잡아내고, 달러 예산은 단계당 많이 쓰는 에이전트를 잡아냅니다. 둘 다 필요합니다.
가격은 한 곳에 있습니다. PRICE_IN와 PRICE_OUT은 자리 표시자 숫자입니다. 가격 페이지에서 모델의 현재 값을 복사하세요. 현재 DeepSeek-V4-Flash는 1M 입력 토큰당 $0.015이고 1M 출력 토큰당 $0.10입니다.
40단계 실행이 합산되는 값#
에이전트가 40단계를 거치고, 컨텍스트가 커지기 때문에 평균 단계가 6,000 입력 토큰을 보내고 400 토큰을 돌려받는다고 가정해 봅시다. 그러면 작업에 대해 40 × 6,000 = 240,000 입력 토큰과 40 × 400 = 16,000 출력 토큰입니다.

DeepSeek-V4-Flash에서 비용은 $0.0052입니다. 표는 동일한 워크로드를 다른 모델에서 실행하고 OpenRouter의 참조 가격을 우리 가격 옆에 놓습니다:
| 모델 | 입력 $/1M | 출력 $/1M | 비용 | OpenRouter |
|---|---|---|---|---|
| GLM-5.3-Flash | $0.02 | $0.04 | $0.0054 | $0.0111 |
| DeepSeek-V4-Flash | $0.015 | $0.10 | $0.0052 | $0.0139 |
| MiniMax-M2.7 | $0.05 | $0.15 | $0.0144 | $0.0912 |
총 입력 240,000개 및 출력 16,000개 토큰입니다. 100만 토큰당 가격은 현재 기준이며 당사 요금제에 따라 함께 변경됩니다. OpenRouter 열은 동일한 작업량에 대한 참고 가격을 보여줍니다.
입력과 출력을 함께 계산한 총계에서 DeepSeek-V4-Flash는 이 작업에 대해 OpenRouter보다 저렴합니다. 토큰이 어디에 있는지도 주목하세요: 256,000 중 240,000이 입력입니다. 에이전트가 끌고 다니는 컨텍스트가 청구서이며, 이것이 로그의 단계당 in= 숫자가 주시해야 할 값인 이유입니다. 위 예제의 작업당 $0.50 예산은 목표가 아니라 폭주에 대한 상한입니다.
모델 전환은 MODEL을 변경하는 것만을 의미합니다. CheapTokens 문서에서 ID를 고르세요: 공급자마다 이름이 다르며, 가격 라벨의 짧은 ID는 API에서 작동하지 않습니다. 긴 에이전트 루프의 경우 DeepSeek-V4-Flash는 하루 중 어느 시간에도 하나의 가격이므로 작업 비용이 정오와 오전 3시에 동일합니다. 한도는 DeepSeek 페이지를 참조하세요.
모델이 사고할 때#
답변이 보이는 텍스트보다 토큰이 훨씬 길다면 reasoning_tokens을 확인하세요. 사고 토큰은 출력으로 계산되므로, 추론이 많은 단계는 보이는 텍스트가 시사하는 것보다 훨씬 많은 출력 토큰을 생성할 수 있고, 출력으로 청구됩니다. 위의 미터는 API가 필드를 반환할 때 meter.reasoning에 이를 수집하고, 그렇지 않으면 0으로 두므로 누락된 키로 절대 충돌하지 않습니다. 출력 중 얼마나 사고였는지 보고 싶다면 out= 옆에 출력하세요. 이유는 추론 노력과 사고 토큰에 설명되어 있습니다.
스트리밍: usage는 마지막에 옵니다#
에이전트가 스트리밍하면 일반 청크에는 usage이 없습니다. OpenAI 형식에서는 stream_로 요청합니다. DeepSeek의 레퍼런스(2026년 10월 26일 확인)에 따르면 이 옵션은 stream=True와 함께만 작동하고(그렇지 않으면 API가 400을 응답), usage은 하나를 제외한 모든 청크에서 null이며, data: [DONE] 전의 마지막 청크가 전체 요청의 합계를 담고 있습니다. 따라서 스트리밍 루프에서는 usage가 비어 있지 않은 청크에서 meter.add을 한 번 호출하세요. 위의 비스트리밍 루프가 시작하기에 가장 간단한 곳입니다; Python에서 스트리밍에 루프 자체가 있습니다.
숫자가 잘못 보인다면#
usage이 None입니다.include_usage없이 스트리밍하고 있습니다. 추가하거나 비스트리밍 호출로 전환하세요.- 총계가 잔액 감소보다 낮습니다. 모든 호출이 미터를 통과하는지 확인하세요: 재시도, 요약 호출 또는
run_task밖에서 이루어진 도구 선택 호출은 놓치기 쉽습니다.client.를 하나의 함수로 감싸고 모든 것을 그리로 라우팅하세요.chat. completions. create - 단계당 비용이 계속 올라갑니다. 모든 것을
messages에 추가하는 루프에서는 정상입니다. 오래된 도구 출력을 잘라내거나 히스토리를 요약하세요; 컨텍스트 밖의 도구 출력에 그 아이디어가 있습니다. BudgetExceeded가 2단계에서 발생합니다. 예산이 한 단계보다 작습니다.PRICE_IN이 1K당이 아니라 1M 토큰당인지 확인하세요.
숫자를 저장하려면 meter.steps, meter.tokens_in, meter.tokens_out, meter.cost을 작업당 하나의 JSON 줄로 작성하세요. 일주일치 그 줄들은 에이전트의 실제 단계당 토큰을 알려주며, 이는 이후 모든 추정에 넣어야 할 숫자입니다. 모델, 가격 및 첫 요청은 문서에 있습니다; 도구 호출은 Python에서 함수 호출에서 다룹니다.



