LLM 앱 개발 실전 #12 비용, 평가, 관측

4 분 소요

11편까지 기능을 만드는 법을 다뤘습니다. 그런데 만든 앱을 실제로 운영하려면 세 가지가 더 필요합니다. 비용을 가늠하고 줄이는 일, 답의 품질을 측정하는 일, 그리고 무슨 일이 일어나는지 들여다보는 일입니다. 이번 글에서는 이 운영의 토대를 정리합니다.

토큰 비용 가늠하기 #

LLM 비용은 주고받은 토큰 양에 비례합니다. 호출하기 전에 입력이 몇 토큰인지 미리 확인하려면 토큰 계산 기능을 씁니다.

count_tokens.py
import anthropic

client = anthropic.Anthropic()

result = client.messages.count_tokens(
    model="claude-sonnet-4-6",
    messages=[{"role": "user", "content": open("long_document.txt").read()}],
)
print(result.input_tokens)  # 입력 토큰 수

토큰 수는 모델마다 다르게 셀 수 있으므로, 실제로 쓸 모델을 지정해 잽니다. 응답을 받은 뒤에는 response.usage에서 실제 사용량을 확인합니다. input_tokensoutput_tokens로 한 번의 호출이 얼마였는지 추적할 수 있습니다.

프롬프트 캐싱으로 비용 줄이기 #

RAG나 긴 system 프롬프트처럼, 매 호출에서 같은 내용이 앞부분에 반복된다면 캐싱으로 큰 비용을 아낄 수 있습니다. 한 번 캐시된 부분은 다음 호출에서 훨씬 싸게 처리됩니다.

prompt_caching.py
response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    system=[
        {
            "type": "text",
            "text": large_shared_context,  # 매번 같은 큰 맥락
            "cache_control": {"type": "ephemeral"},
        }
    ],
    messages=[{"role": "user", "content": "질문"}],
)

print(response.usage.cache_read_input_tokens)  # 캐시에서 읽은 토큰

캐싱은 앞부분이 글자 단위로 똑같을 때만 적용됩니다. 그래서 변하지 않는 내용(고정된 지침, 공유 문서)을 앞에 두고, 매번 바뀌는 내용(질문, 시각)을 뒤에 둡니다. 제대로 동작하는지는 usage.cache_read_input_tokens로 확인합니다. 이 값이 계속 0이면 앞부분에 매번 바뀌는 무언가가 끼어 캐시가 깨지고 있다는 신호입니다.

모델로 비용 조절하기 #

2편에서 본 세 등급을 비용 관점에서 다시 봅니다. 모든 호출에 가장 강력한 모델을 쓸 필요는 없습니다. 작업 성격에 맞춰 등급을 나누면 비용이 크게 줄어듭니다.

  • 단순 분류, 짧은 추출 → 가장 저렴한 Haiku
  • 대부분의 실무 작업 → 균형 등급 Sonnet
  • 까다로운 추론, 긴 에이전트 작업 → 가장 강력한 Opus

한 앱 안에서도 단계마다 다른 모델을 쓸 수 있습니다. 예를 들어 분류는 Haiku로, 최종 답 생성은 Sonnet으로 나누는 식입니다.

답의 품질 평가하기 #

프롬프트를 바꿨을 때 답이 나아졌는지 어떻게 알까요? 눈으로 몇 개 보는 것만으로는 부족합니다. 평가를 자동화하는 한 방법은, 또 다른 LLM 호출에 채점을 맡기는 것입니다. 이를 LLM-as-judge라고 합니다.

llm_judge.py
def judge(question: str, answer: str) -> str:
    prompt = f"""아래 답변이 질문에 정확하고 도움이 되는지 평가해줘.
'좋음' 또는 '나쁨' 한 단어로만 답해.

질문: {question}
답변: {answer}"""

    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=10,
        messages=[{"role": "user", "content": prompt}],
    )
    return next(b.text for b in response.content if b.type == "text")

질문과 기대 답의 묶음(평가셋)을 미리 만들어 두고, 프롬프트나 모델을 바꿀 때마다 이 평가를 돌리면, 변경이 품질을 올렸는지 내렸는지를 숫자로 비교할 수 있습니다. 5편에서 본 구조화된 출력으로 채점 결과를 받으면 집계가 더 쉬워집니다.

동작을 들여다보기 #

LLM 앱은 같은 입력에도 답이 달라지므로, 문제가 생겼을 때 무슨 일이 있었는지 보려면 기록이 필요합니다. 최소한 입력 프롬프트, 받은 답, 사용한 토큰을 남깁니다. 문제를 Anthropic에 알릴 때를 대비해 요청 식별자도 함께 기록해 두면 좋습니다.

logging.py
response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=messages,
)

log({
    "request_id": response._request_id,            # 문제 추적용 식별자
    "input_tokens": response.usage.input_tokens,
    "output_tokens": response.usage.output_tokens,
    "stop_reason": response.stop_reason,
})

이렇게 쌓은 기록으로 비용 추이를 보고, 답이 자주 잘리는지(stop_reason), 특정 입력에서 문제가 생기는지를 추적합니다.

배치로 더 줄이기 #

급하지 않은 대량 작업이라면 배치 API로 비용을 절반으로 줄일 수 있습니다. 실시간 응답 대신, 많은 요청을 한꺼번에 맡기고 결과를 나중에(보통 한 시간 안에) 받는 방식입니다. 토큰 요금이 50% 할인됩니다.

batch.py
from anthropic.types.message_create_params import MessageCreateParamsNonStreaming
from anthropic.types.messages.batch_create_params import Request

batch = client.messages.batches.create(
    requests=[
        Request(
            custom_id=f"item-{i}",
            params=MessageCreateParamsNonStreaming(
                model="claude-sonnet-4-6",
                max_tokens=256,
                messages=[{"role": "user", "content": text}],
            ),
        )
        for i, text in enumerate(texts)
    ]
)

수천 건의 리뷰를 분류하거나 문서를 한꺼번에 요약하는 것처럼, 즉시 답이 필요 없는 작업에 잘 맞습니다. 사용자가 기다리는 대화형 호출에는 맞지 않지만, 백그라운드 일괄 처리에서는 비용을 크게 아낍니다.

흔히 걸려 넘어지는 곳 #

  • 캐시가 깨지는 걸 모른다 — system 앞부분에 현재 시각이나 무작위 값이 들어가면 매번 캐시가 깨집니다. cache_read_input_tokens가 0인지 확인합니다.
  • 평가 없이 프롬프트를 고친다 — 평가셋 없이 감으로 프롬프트를 바꾸면, 한 경우는 나아지고 다른 경우는 나빠지는 걸 놓칩니다. 작게라도 평가셋을 둡니다.
  • 아무것도 기록하지 않는다 — 기록이 없으면 문제가 생겼을 때 재현도 추적도 어렵습니다. 최소한의 사용량과 식별자는 남깁니다.

마무리 #

이번 글에서는 앱을 운영하는 데 필요한 비용, 평가, 관측을 정리했습니다.

  • 토큰을 미리 재고(count_tokens), 반복되는 앞부분은 캐싱으로 줄이며, 작업에 맞는 모델 등급을 고릅니다.
  • LLM-as-judge와 평가셋으로 변경이 품질에 미친 영향을 숫자로 봅니다.
  • 사용량과 요청 식별자를 기록해 비용과 문제를 추적합니다.

이제 기능과 운영의 토대가 모두 모였습니다. 마지막 글인 “LLM 앱 개발 실전 #13 실전 프로젝트"에서는 지금까지의 조각들을 하나로 묶어, 사내 문서에 답하는 Q&A 봇을 처음부터 끝까지 만들어 봅니다.

X