콘텐츠로 이동

비용 최적화와 모델 라우팅

소규모 프로젝트에서 에이전트 비용은 무시할 수 있는 수준입니다. 하지만 팀이 에이전트를 적극 활용하기 시작하면 비용이 빠르게 증가합니다.

개인 사용: $10~50/월 → 무시 가능
소규모 팀: $200~500/월 → 최적화 고려
프로덕션 팀: $2,000+/월 → 전략적 최적화 필수

비용 최적화는 단순히 돈을 아끼는 것이 아닙니다. 올바른 도구를 올바른 작업에 사용하는 엔지니어링 판단의 문제입니다.

모델 계층평가할 작업선택 기준
Haiku분류, 요약, 포맷 변환실제 오류율과 입력 길이에 따른 단가
Sonnet일반 코드 작성, 디버깅작업당 성공 비용·지연
Opus아키텍처 설계, 복잡한 분석추가 추론이 만드는 품질 차이
harness/model-router.ts
type TaskComplexity = 'simple' | 'standard' | 'complex'
interface RoutingRule {
keywords: string[]
maxTokens: number
complexity: TaskComplexity
}
const ROUTING_RULES: RoutingRule[] = [
{
keywords: ['classify', 'summarize', 'format', 'extract', 'translate'],
maxTokens: 1000,
complexity: 'simple'
},
{
keywords: ['implement', 'fix', 'refactor', 'test', 'debug'],
maxTokens: 4000,
complexity: 'standard'
},
{
keywords: ['architect', 'design', 'analyze', 'review', 'optimize'],
maxTokens: 8000,
complexity: 'complex'
}
]
function routeTask(task: string, contextSize: number): string {
const lowerTask = task.toLowerCase()
for (const rule of ROUTING_RULES) {
const matches = rule.keywords.some(kw => lowerTask.includes(kw))
if (matches && contextSize <= rule.maxTokens) {
return modelForComplexity(rule.complexity)
}
}
return 'claude-sonnet-5-5' // 기본값
}
function modelForComplexity(complexity: TaskComplexity): string {
const models: Record<TaskComplexity, string> = {
simple: 'claude-haiku-5-5',
standard: 'claude-sonnet-5-5',
complex: 'claude-opus-5-5'
}
return models[complexity]
}

KV-cache(Key-Value Cache)는 반복되는 컨텍스트를 캐싱하여 API 비용을 대폭 줄이는 기술입니다.

예시: Claude Sonnet 5.5, 동일한 10,000 입력 토큰을 두 번 사용
일반 입력 단가: $2 / 1M tokens
5분 캐시 쓰기: $2.50 / 1M tokens
캐시 읽기: $0.10 / 1M tokens
캐시 없음: 2 × 10,000 × $2 / 1M = $0.040
캐시 생성·읽기: 10,000 × ($2.50 + $0.10)/1M = $0.026

이는 반복 prefix의 입력 비용 예시입니다. 출력, 캐시되지 않은 입력과 기타 도구 요금은 별도로 더합니다. 최초 캐시 쓰기는 일반 입력보다 비싸므로 재사용 횟수·TTL·최소 입력 길이를 함께 고려해야 합니다.

harness/cached-client.ts
import Anthropic from '@anthropic-ai/sdk'
const client = new Anthropic()
async function runWithCache(
systemPrompt: string,
userMessage: string
): Promise<string> {
const response = await client.messages.create({
model: 'claude-sonnet-5-5',
max_tokens: 4096,
system: [
{
type: 'text',
text: systemPrompt,
// 시스템 프롬프트를 캐시 대상으로 지정
cache_control: { type: 'ephemeral' }
}
],
messages: [
{ role: 'user', content: userMessage }
]
})
// 캐시 사용 통계 로깅
const usage = response.usage
console.info('Cache stats:', {
inputTokens: usage.input_tokens,
cacheCreationTokens: usage.cache_creation_input_tokens,
cacheReadTokens: usage.cache_read_input_tokens
})
return response.content[0].type === 'text' ? response.content[0].text : ''
}
높은 캐시 히트율을 위한 설계:
1. 정적 컨텐츠를 앞에 배치
[시스템 프롬프트 - 캐시] + [사용자 입력 - 비캐시]
2. AGENTS.md를 시스템 프롬프트에 포함
매 요청마다 동일한 컨텐츠 → 높은 히트율
3. 대화 히스토리 전략
압축 시 prefix가 바뀌어 재캐싱되는 비용과 토큰 절감을 비교
harness/token-budget.ts
interface TokenBudget {
maxInputTokens: number
maxOutputTokens: number
warningThreshold: number // 경고 발생 비율
}
const TOKEN_BUDGETS: Record<string, TokenBudget> = {
codeGeneration: {
maxInputTokens: 8_000,
maxOutputTokens: 4_000,
warningThreshold: 0.8
},
codeReview: {
maxInputTokens: 16_000,
maxOutputTokens: 2_000,
warningThreshold: 0.8
},
architectureAnalysis: {
maxInputTokens: 32_000,
maxOutputTokens: 8_000,
warningThreshold: 0.9
}
}
function validateTokenUsage(
taskType: string,
inputTokens: number
): { allowed: boolean; warning: string | null } {
const budget = TOKEN_BUDGETS[taskType]
if (!budget) return { allowed: true, warning: null }
if (inputTokens > budget.maxInputTokens) {
return {
allowed: false,
warning: `토큰 예산 초과: ${inputTokens} > ${budget.maxInputTokens}`
}
}
if (inputTokens > budget.maxInputTokens * budget.warningThreshold) {
return {
allowed: true,
warning: `토큰 예산 ${Math.round(inputTokens / budget.maxInputTokens * 100)}% 사용 중`
}
}
return { allowed: true, warning: null }
}
harness/cost-tracker.ts
interface CostRecord {
timestamp: Date
model: string
taskType: string
inputTokens: number // 캐시되지 않은 input_tokens
outputTokens: number
cacheReadTokens: number
cacheWrite5mTokens: number
cacheWrite1hTokens: number
estimatedCost: number
}
// USD / 1M tokens, Claude API standard pricing, accessed 2026-10-23.
const COST_PER_1M: Record<string, { input: number; output: number; cacheRead: number }> = {
'claude-haiku-5-5': { input: 0.10, output: 0.50, cacheRead: 0.01 },
'claude-sonnet-5-5': { input: 2, output: 10, cacheRead: 0.10 },
'claude-opus-5-5': { input: 4, output: 20, cacheRead: 0.20 },
}
function calculateCost(record: Omit<CostRecord, 'estimatedCost'>): number {
const base = COST_PER_1M[record.model]
if (!base) throw new Error(`Missing pricing for ${record.model}`)
const totalInput = record.inputTokens + record.cacheReadTokens +
record.cacheWrite5mTokens + record.cacheWrite1hTokens
const rates = record.model === 'claude-haiku-5-5' && totalInput > 100_000
? { input: 0.50, output: 2.50, cacheRead: 0.05 }
: base
return (
record.inputTokens * rates.input +
record.outputTokens * rates.output +
record.cacheReadTokens * rates.cacheRead +
record.cacheWrite5mTokens * rates.input * 1.25 +
record.cacheWrite1hTokens * rates.input * 2
) / 1_000_000
}

Anthropic usage의 input_tokens는 캐시 읽기·쓰기 토큰과 별도입니다. 캐시 생성 총량과 5분·1시간 세부량을 동시에 합산하면 이중 과금 계산이 됩니다. 실제 응답의 usage를 위의 서로 배타적인 범주로 매핑합니다. 새 모델·가격을 사용할 때는 가격표와 계산 예제를 함께 갱신합니다.

.github/workflows/cost-alert.yml
name: Daily Cost Alert
on:
schedule:
- cron: '0 9 * * *' # 매일 오전 9시
jobs:
check-cost:
runs-on: ubuntu-latest
steps:
- name: Check daily spend
run: |
DAILY_COST=$(node scripts/get-daily-cost.js)
BUDGET=50 # $50/일 예산
if (( $(echo "$DAILY_COST > $BUDGET" | bc -l) )); then
echo "::warning::일일 비용 초과: $${DAILY_COST} (예산: $${BUDGET})"
# Slack 알림 전송
curl -X POST $SLACK_WEBHOOK \
-d "{\"text\": \"AI 에이전트 일일 비용 경고: \$${DAILY_COST}\"}"
fi
항목확인할 지표
모델 라우팅작업별 성공률, 재시도율, 입력·출력 토큰 비용
프롬프트 캐싱캐시 적중률, 캐시 읽기·쓰기 단가, 지연시간
컨텍스트 압축정답률·근거 누락률, 전달 토큰 수
배치 처리대기시간, 처리량, 실패한 재시도의 비용
토큰 예산예산 초과 차단 횟수와 중단된 작업의 품질

비용 최적화는 모델 라우팅, 프롬프트 캐싱, 토큰 예산 관리의 세 축으로 구성됩니다. 반복되는 컨텍스트가 있다고 해서 항상 비용 이점이 생기는 것은 아니므로, 모델·프롬프트·캐시 정책·예산을 바꿀 때마다 성공률, 지연시간, 토큰 비용을 같은 작업 묶음에서 비교합니다.