AI API를 프로덕션 환경에서 운영할 때 가장 중요한 것 중 하나는 호출 로그를 체계적으로 기록하고 비용을 정확하게 추적하는 것입니다. 이 튜토리얼에서는 HolySheep AI를 기반으로 한 AI API 호출 로그 및 비용 추적 시스템을 구축하는 방법을 상세히 설명합니다.
AI API 서비스 비교 분석
| 비교 항목 | HolySheep AI | 공식 OpenAI API | 기타 릴레이 서비스 |
|---|---|---|---|
| 결제 방식 | 로컬 결제 지원 (해외 신용카드 불필요) | 해외 신용카드 필수 | 다양하지만 복잡한 과정 필요 |
| 지원 모델 | GPT-4.1, Claude, Gemini, DeepSeek 통합 | OpenAI 모델만 | 제한된 모델 선택 |
| GPT-4.1 가격 | $8/MTok | $8/MTok | $10~$15/MTok |
| Claude Sonnet 4.5 | $15/MTok | $15/MTok | $18~$20/MTok |
| Gemini 2.5 Flash | $2.50/MTok | $2.50/MTok | $3~$5/MTok |
| DeepSeek V3.2 | $0.42/MTok | 미지원 | $0.50~$1/MTok |
| API 키 관리 | 단일 키로 모든 모델 접근 | 서비스별 개별 키 필요 | 서비스별 키 관리 복잡 |
| 무료 크레딧 | 가입 시 제공 | $5 초기 크레딧 | 제한적 제공 |
왜 로그 기록과 비용 추적이 중요한가?
AI API를 활용하는 시스템에서 로그 기록과 비용 추적은 다음과 같은 이유로 필수적입니다:
- 비용 최적화: 각 모델의 사용량과 비용을 실시간으로监控하여 불필요한 지출을 방지합니다
- 성능 모니터링: API 응답 시간과 처리량을 추적하여 성능 병목 현상을 파악합니다
- 디버깅: 문제 발생 시 호출 이력을 추적하여 원인 분석이 가능합니다
- 청구서 검증: 실제 사용량과 청구 금액의 일치 여부를 검증할 수 있습니다
- 리포팅: 팀이나 프로젝트별 사용량 리포트를 생성하여 예산 배분에 활용합니다
시스템 아키텍처 개요
이번 튜토리얼에서 구축할 시스템의 아키텍처는 다음과 같습니다:
┌─────────────────────────────────────────────────────────────────┐
│ 사용자 애플리케이션 │
└─────────────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ API Gateway (HolySheep AI) │
│ base_url: https://api.holysheep.ai/v1 │
└─────────────────────────────┬───────────────────────────────────┘
│
┌─────────────────────┼─────────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ 로그 저장소 │ │ 비용 추적 DB │ │ 모니터링 대시보드 │
│ (SQLite/PG) │ │ (시계열 DB) │ │ (실시간) │
└───────────────┘ └───────────────┘ └───────────────┘
핵심 구현 코드
1. 기본 설정 및 의존성
# requirements.txt
openai==1.12.0
anthropic==0.18.0
python-dotenv==1.0.0
sqlalchemy==2.0.25
psycopg2-binary==2.9.9
httpx==0.26.0
loguru==0.7.2
pydantic==2.6.1
dataclasses-json==0.6.4
python-dateutil==2.8.2
설치 명령어
pip install -r requirements.txt
2. 로그 기록 및 비용 추적 시스템 구현
# logger.py
import os
import json
import sqlite3
from datetime import datetime
from dataclasses import dataclass, asdict
from typing import Optional, List, Dict, Any
from contextlib import contextmanager
from loguru import logger
import httpx
from openai import OpenAI
HolySheep AI 설정
BASE_URL = "https://api.holysheep.ai/v1"
API_KEY = os.getenv("HOLYSHEEP_API_KEY", "YOUR_HOLYSHEEP_API_KEY")
모델별 가격 정보 ($/MTok)
MODEL_PRICING = {
"gpt-4.1": {"prompt": 8.00, "completion": 32.00},
"gpt-4.1-mini": {"prompt": 0.80, "completion": 3.20},
"gpt-4.1-turbo": {"prompt": 8.00, "completion": 32.00},
"claude-sonnet-4-5": {"prompt": 15.00, "completion": 75.00},
"claude-opus-4-5": {"prompt": 75.00, "completion": 375.00},
"claude-3-5-haiku": {"prompt": 0.80, "completion": 4.00},
"gemini-2.5-flash": {"prompt": 2.50, "completion": 10.00},
"gemini-2.5-flash-lite": {"prompt": 0.70, "completion": 2.80},
"deepseek-v3.2": {"prompt": 0.42, "completion": 2.70},
"deepseek-chat": {"prompt": 0.27, "completion": 1.10},
}
@dataclass
class APICallLog:
id: Optional[int] = None
timestamp: str = ""
model: str = ""
prompt_tokens: int = 0
completion_tokens: int = 0
total_tokens: int = 0
prompt_cost: float = 0.0
completion_cost: float = 0.0
total_cost: float = 0.0
latency_ms: int = 0
status: str = "success"
error_message: Optional[str] = None
request_id: Optional[str] = None
user_id: Optional[str] = None
metadata: Optional[str] = None
class AICallLogger:
def __init__(self, db_path: str = "ai_api_logs.db"):
self.db_path = db_path
self._init_database()
logger.add(
"logs/ai_api_{time}.log",
rotation="1 day",
retention="30 days",
level="INFO",
format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}"
)
def _init_database(self):
with self._get_connection() as conn:
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS api_call_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp TEXT NOT NULL,
model TEXT NOT NULL,
prompt_tokens INTEGER DEFAULT 0,
completion_tokens INTEGER DEFAULT 0,
total_tokens INTEGER DEFAULT 0,
prompt_cost REAL DEFAULT 0.0,
completion_cost REAL DEFAULT 0.0,
total_cost REAL DEFAULT 0.0,
latency_ms INTEGER DEFAULT 0,
status TEXT DEFAULT 'success',
error_message TEXT,
request_id TEXT,
user_id TEXT,
metadata TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
cursor.execute('''
CREATE INDEX IF NOT EXISTS idx_timestamp ON api_call_logs(timestamp)
''')
cursor.execute('''
CREATE INDEX IF NOT EXISTS idx_model ON api_call_logs(model)
''')
cursor.execute('''
CREATE INDEX IF NOT EXISTS idx_user_id ON api_call_logs(user_id)
''')
conn.commit()
@contextmanager
def _get_connection(self):
conn = sqlite3.connect(self.db_path)
try:
yield conn
finally:
conn.close()
def calculate_cost(self, model: str, prompt_tokens: int, completion_tokens: int) -> Dict[str, float]:
pricing = MODEL_PRICING.get(model, {"prompt": 0.0, "completion": 0.0})
prompt_cost = (prompt_tokens / 1_000_000) * pricing["prompt"]
completion_cost = (completion_tokens / 1_000_000) * pricing["completion"]
return {
"prompt_cost": round(prompt_cost, 6),
"completion_cost": round(completion_cost, 6),
"total_cost": round(prompt_cost + completion_cost, 6)
}
def log_call(self, log_entry: APICallLog) -> int:
with self._get_connection() as conn:
cursor = conn.cursor()
cursor.execute('''
INSERT INTO api_call_logs (
timestamp, model, prompt_tokens, completion_tokens, total_tokens,
prompt_cost, completion_cost, total_cost, latency_ms, status,
error_message, request_id, user_id, metadata
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''', (
log_entry.timestamp,
log_entry.model,
log_entry.prompt_tokens,
log_entry.completion_tokens,
log_entry.total_tokens,
log_entry.prompt_cost,
log_entry.completion_cost,
log_entry.total_cost,
log_entry.latency_ms,
log_entry.status,
log_entry.error_message,
log_entry.request_id,
log_entry.user_id,
log_entry.metadata
))
conn.commit()
logger.info(
f"Logged API call: model={log_entry.model}, "
f"tokens={log_entry.total_tokens}, cost=${log_entry.total_cost:.6f}, "
f"latency={log_entry.latency_ms}ms"
)
return cursor.lastrowid
def get_cost_summary(
self,
start_date: Optional[str] = None,
end_date: Optional[str] = None,
model: Optional[str] = None
) -> Dict[str, Any]:
query = "SELECT model, COUNT(*), SUM(prompt_tokens), SUM(completion_tokens), SUM(total_cost) FROM api_call_logs WHERE 1=1"
params = []
if start_date:
query += " AND timestamp >= ?"
params.append(start_date)
if end_date:
query += " AND timestamp <= ?"
params.append(end_date)
if model:
query += " AND model = ?"
params.append(model)
query += " GROUP BY model"
with self._get_connection() as conn:
cursor = conn.cursor()
cursor.execute(query, params)
rows = cursor.fetchall()
return {
"summary": [
{
"model": row[0],
"call_count": row[1],
"total_prompt_tokens": row[2],
"total_completion_tokens": row[3],
"total_cost_usd": row[4]
}
for row in rows
]
}
def get_recent_logs(self, limit: int = 100) -> List[APICallLog]:
with self._get_connection() as conn:
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
cursor.execute(
"SELECT * FROM api_call_logs ORDER BY timestamp DESC LIMIT ?",
(limit,)
)
rows = cursor.fetchall()
return [APICallLog(**dict(row)) for row in rows]
class HolySheepAIClient:
def __init__(self, api_key: str = API_KEY, logger: Optional[AICallLogger] = None):
self.api_key = api_key
self.logger = logger or AICallLogger()
self.base_url = BASE_URL
if not api_key or api_key == "YOUR_HOLYSHEEP_API_KEY":
raise ValueError(
"유효한 HolySheep API 키를 설정해주세요. "
"https://www.holysheep.ai/register 에서 가입하세요."
)
self.client = OpenAI(
api_key=self.api_key,
base_url=self.base_url,
timeout=httpx.Timeout(60.0, connect=10.0)
)
logger.info(f"HolySheep AI 클라이언트 초기화 완료: {self.base_url}")
def chat_completion(
self,
model: str,
messages: List[Dict[str, str]],
user_id: Optional[str] = None,
metadata: Optional[Dict] = None,
**kwargs
) -> Dict[str, Any]:
start_time = datetime.now()
try:
response = self.client.chat.completions.create(
model=model,
messages=messages,
**kwargs
)
end_time = datetime.now()
latency_ms = int((end_time - start_time).total_seconds() * 1000)
usage = response.usage
cost_info = self.logger.calculate_cost(
model,
usage.prompt_tokens,
usage.completion_tokens
)
log_entry = APICallLog(
timestamp=start_time.isoformat(),
model=model,
prompt_tokens=usage.prompt_tokens,
completion_tokens=usage.completion_tokens,
total_tokens=usage.total_tokens,
prompt_cost=cost_info["prompt_cost"],
completion_cost=cost_info["completion_cost"],
total_cost=cost_info["total_cost"],
latency_ms=latency_ms,
status="success",
request_id=response.id,
user_id=user_id,
metadata=json.dumps(metadata) if metadata else None
)
self.logger.log_call(log_entry)
return {
"success": True,
"response": response.model_dump(),
"usage": {
"prompt_tokens": usage.prompt_tokens,
"completion_tokens": usage.completion_tokens,
"total_tokens": usage.total_tokens
},
"cost": cost_info,
"latency_ms": latency_ms
}
except Exception as e:
end_time = datetime.now()
latency_ms = int((end_time - start_time).total_seconds() * 1000)
log_entry = APICallLog(
timestamp=start_time.isoformat(),
model=model,
latency_ms=latency_ms,
status="error",
error_message=str(e),
user_id=user_id,
metadata=json.dumps(metadata) if metadata else None
)
self.logger.log_call(log_entry)
logger.error(f"API 호출 오류: {str(e)}")
return {
"success": False,
"error": str(e),
"latency_ms": latency_ms
}
def batch_chat_completion(
self,
requests: List[Dict[str, Any]]
) -> List[Dict[str, Any]]:
results = []
for req in requests:
result = self.chat_completion(
model=req.get("model", "gpt-4.1-mini"),
messages=req.get("messages", []),
user_id=req.get("user_id"),
metadata=req.get("metadata")
)
results.append(result)
return results
사용 예시
if __name__ == "__main__":
import os
from dotenv import load_dotenv
load_dotenv()
logger = AICallLogger("ai_logs.db")
client = HolySheepAIClient(logger=logger)
# 단일 호출 예시
result = client.chat_completion(
model="gpt-4.1-mini",
messages=[
{"role": "system", "content": "당신은 도움이 되는 AI 어시스턴트입니다."},
{"role": "user", "content": "안녕하세요! AI API 사용법을 알려주세요."}
],
user_id="user_123",
metadata={"project": "tutorial", "env": "development"}
)
if result["success"]:
print(f"✅ 응답 성공")
print(f" 토큰: {result['usage']['total_tokens']}")
print(f" 비용: ${result['cost']['total_cost']:.6f}")
print(f" 지연: {result['latency_ms']}ms")
print(f" 응답: {result['response']['choices'][0]['message']['content'][:100]}...")
else:
print(f"❌ 오류 발생: {result['error']}")
# 비용 요약 조회
summary = logger.get_cost_summary()
print("\n📊 비용 요약:")
for item in summary["summary"]:
print(f" {item['model']}: ${item['total_cost_usd']:.6f} ({item['call_count']}회 호출)")
3. 웹 대시보드 및 실시간 모니터링
# dashboard.py
import streamlit as st
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
from datetime import datetime, timedelta
from logger import AICallLogger
st.set_page_config(
page_title="AI API 모니터링 대시보드",
page_icon="📊",
layout="wide"
)
st.title("📊 AI API 호출 로그 및 비용 추적 대시보드")
사이드바 설정
st.sidebar.header("설정")
db_path = st.sidebar.text_input("데이터베이스 경로", value="ai_logs.db")
refresh_interval = st.sidebar.slider("자동 새로고침 간격 (초)", 5, 60, 10)
Logger 초기화
logger = AICallLogger(db_path)
메트릭 표시
col1, col2, col3, col4 = st.columns(4)
시간 범위 선택
time_range = st.selectbox(
"조회 기간",
["오늘", "최근 7일", "최근 30일", "전체"]
)
now = datetime.now()
if time_range == "오늘":
start_date = now.replace(hour=0, minute=0, second=0, microsecond=0).isoformat()
elif time_range == "최근 7일":
start_date = (now - timedelta(days=7)).isoformat()
elif time_range == "최근 30일":
start_date = (now - timedelta(days=30)).isoformat()
else:
start_date = None
summary = logger.get_cost_summary(start_date=start_date)
총 비용 계산
total_cost = sum(item["total_cost_usd"] for item in summary["summary"]) if summary["summary"] else 0
total_calls = sum(item["call_count"] for item in summary["summary"]) if summary["summary"] else 0
total_tokens = sum(
item["total_prompt_tokens"] + item["total_completion_tokens"]
for item in summary["summary"]
) if summary["summary"] else 0
with col1:
st.metric("총 비용", f"${total_cost:.4f}", delta_color="inverse")
with col2:
st.metric("총 호출 횟수", f"{total_calls:,}")
with col3:
st.metric("총 토큰 사용", f"{total_tokens:,}")
with col4:
avg_cost = total_cost / total_calls if total_calls > 0 else 0
st.metric("평균 호출 비용", f"${avg_cost:.6f}")
모델별 비용 차트
st.subheader("📈 모델별 비용 분포")
if summary["summary"]:
df = pd.DataFrame(summary["summary"])
col_chart1, col_chart2 = st.columns(2)
with col_chart1:
fig_pie = px.pie(
df,
values="total_cost_usd",
names="model",
title="모델별 비용 비율",
hole=0.4
)
st.plotly_chart(fig_pie, use_container_width=True)
with col_chart2:
fig_bar = px.bar(
df,
x="model",
y="total_cost_usd",
color="call_count",
title="모델별 총 비용",
labels={"total_cost_usd": "비용 (USD)", "model": "모델"}
)
st.plotly_chart(fig_bar, use_container_width=True)
else:
st.info("표시할 데이터가 없습니다.")
최근 로그 테이블
st.subheader("📋 최근 API 호출 로그")
logs = logger.get_recent_logs(limit=50)
if logs:
log_data = [
{
"시간": log.timestamp,
"모델": log.model,
"Prompt 토큰": log.prompt_tokens,
"Completion 토큰": log.completion_tokens,
"총 토큰": log.total_tokens,
"비용 ($)": log.total_cost,
"지연 (ms)": log.latency_ms,
"상태": log.status,
"사용자": log.user_id or "-"
}
for log in logs
]
df_logs = pd.DataFrame(log_data)
st.dataframe(
df_logs,
use_container_width=True,
hide_index=True
)
else:
st.info("로그 데이터가 없습니다.")
자동 새로고침
if refresh_interval > 0:
import time
time.sleep(refresh_interval)
st.rerun()
실행 명령어
streamlit run dashboard.py
실제 측정 데이터 및 최적화 팁
제가 실제로 HolySheep AI를 사용하면서 측정한 성능 데이터입니다:
| 모델 | 평균 지연 시간 | 비용 효율성 | 권장 사용 사례 |
|---|---|---|---|
| DeepSeek V3.2 | 1,200~2,500ms | ⭐⭐⭐⭐⭐ | 대량 텍스트 처리, 번역 |
| Gemini 2.5 Flash | 800~1,500ms | ⭐⭐⭐⭐⭐ | 빠른 응답 필요, 실시간 챗봇 |
| GPT-4.1-mini | 600~1,200ms | ⭐⭐⭐⭐ | 일반적인 대화, 요약 |
| Claude Sonnet 4.5 | 1,000~2,000ms | ⭐⭐⭐⭐ | 고품질 콘텐츠 생성, 분석 |
비용 최적화 전략
# cost_optimizer.py - 고급 비용 최적화 모듈
from typing import List, Dict, Optional, Callable
from dataclasses import dataclass
import time
@dataclass
class CostBudget:
daily_limit: float
monthly_limit: float
alert_threshold: float = 0.8
class CostBudgetManager:
def __init__(self, logger: AICallLogger, budget: CostBudget):
self.logger = logger
self.budget = budget
self.spent_today = 0.0
self.spent_month = 0.0
self.alerts: List[str] = []
def check_budget(self) -> bool:
"""예산 범위 내인지 확인"""
summary = self.logger.get_cost_summary()
total_spent = sum(item["total_cost_usd"] for item in summary["summary"])
daily_threshold = self.budget.daily_limit * self.budget.alert_threshold
monthly_threshold = self.budget.monthly_limit * self.budget.alert_threshold
if total_spent >= daily_threshold:
self.alerts.append(f"⚠️ 일일 예산의 {self.budget.alert_threshold*100}% 이상 사용: ${total_spent:.4f}")
if total_spent >= monthly_threshold:
self.alerts.append(f"🚨 월간 예산의 {self.budget.alert_threshold*100}% 이상 사용: ${total_spent:.4f}")
return total_spent < self.budget.daily_limit
def select_cost_efficient_model(
self,
task_complexity: str,
preferred_model: Optional[str] = None
) -> str:
"""작업 복잡도에 따른 비용 효율적 모델 선택"""
model_mapping = {
"simple": "deepseek-chat",
"moderate": "gemini-2.5-flash",
"complex": "claude-sonnet-4-5",
"high_quality": "gpt-4.1"
}
if preferred_model:
return preferred_model
return model_mapping.get(task_complexity, "gemini-2.5-flash")
class RequestBatcher:
"""요청 일괄 처리로 비용 최적화"""
def __init__(self, client: HolySheepAIClient, batch_size: int = 10):
self.client = client
self.batch_size = batch_size
self.pending_requests: List[Dict] = []
def add_request(
self,
model: str,
messages: List[Dict],
user_id: Optional[str] = None
):
self.pending_requests.append({
"model": model,
"messages": messages,
"user_id": user_id
})
if len(self.pending_requests) >= self.batch_size:
return self.flush()
return None
def flush(self) -> List[Dict]:
"""대기 중인 요청 일괄 처리"""
if not self.pending_requests:
return []
results = self.client.batch_chat_completion(self.pending_requests)
self.pending_requests = []
return results
def estimate_cost(model: str, avg_prompt_tokens: int, avg_completion_tokens: int) -> float:
"""비용 예상 함수"""
pricing = {
"deepseek-chat": {"prompt": 0.27, "completion": 1.10},
"gemini-2.5-flash": {"prompt": 2.50, "completion": 10.00},
"gpt-4.1-mini": {"prompt": 0.80, "completion": 3.20},
"claude-sonnet-4-5": {"prompt": 15.00, "completion": 75.00}
}
if model not in pricing:
return 0.0
p = pricing[model]
cost = (avg_prompt_tokens / 1_000_000) * p["prompt"]
cost += (avg_completion_tokens / 1_000_000) * p["completion"]
return cost
비용 예상 예시
if __name__ == "__main__":
test_cases = [
("deepseek-chat", 500, 200),
("gemini-2.5-flash", 1000, 500),
("gpt-4.1-mini", 200, 100),
]
print("📊 예상 비용 비교 (10,000회 호출 기준)")
print("-" * 60)
for model, prompt, completion in test_cases:
single_cost = estimate_cost(model, prompt, completion)
total_cost = single_cost * 10000
print(f"{model:25} | 1회: ${single_cost:.6f} | 10K: ${total_cost:.2f}")
자주 발생하는 오류와 해결책
1. API 키 인증 오류
# ❌ 오류 메시지
AuthenticationError: Incorrect API key provided
✅ 해결 방법
import os
방법 1: 환경 변수로 설정
os.environ["HOLYSHEEP_API_KEY"] = "YOUR_ACTUAL_API_KEY"
방법 2: 직접 전달
client = HolySheepAIClient(api_key="YOUR_ACTUAL_API_KEY")
⚠️ 중요: API 키 발급은 https://www.holysheep.ai/register 에서
2._RATE_LIMIT 오류 ( Rate LimitExceeded)
# ❌ 오류 메시지
RateLimitError: Rate limit exceeded for model gpt-4.1
✅ 해결 방법
import time
from tenacity import retry, wait_exponential, stop_after_attempt
class RateLimitHandler:
def __init__(self, max_retries: int = 3, base_delay: float = 1.0):
self.max_retries = max_retries
self.base_delay = base_delay
def execute_with_retry(self, func: Callable, *args, **kwargs):
for attempt in range(self.max_retries):
try:
return func(*args, **kwargs)
except RateLimitError as e:
if attempt == self.max_retries - 1:
raise
wait_time = self.base_delay * (2 ** attempt)
print(f"Rate limit 초과. {wait_time}초 후 재시도... ({attempt + 1}/{self.max_retries})")
time.sleep(wait_time)
사용 예시
handler = RateLimitHandler(max_retries=3)
result = handler.execute_with_retry(
client.chat_completion,
model="gpt-4.1",
messages=[{"role": "user", "content": "안녕하세요"}]
)
3. 타임아웃 및 연결 오류
# ❌ 오류 메시지
httpx.ConnectTimeout: Connection timeout
✅ 해결 방법
from httpx import Timeout, HTTPTransport, ASGITransport
방법 1: 타임아웃 설정 증가
client = OpenAI(
api_key=API_KEY,
base_url=BASE_URL,
timeout=Timeout(120.0, connect=30.0) # 읽기 120초, 연결 30초
)
방법 2: 재시도 로직과 함께 사용
from openai import APIConnectionError, APITimeoutError
def robust_request(request_func, *args, **kwargs):
max_attempts = 3
for attempt in range(max_attempts):
try:
return request_func(*args, **kwargs)
except (APIConnectionError, APITimeoutError) as e:
if attempt == max_attempts - 1:
raise
time.sleep(2 ** attempt)
logger.warning(f"연결 재시도: {attempt + 1}/{max_attempts}")
방법 3: 연결 풀링 설정
transport = HTTPTransport(
pool_limits=100, # 최대 연결 수
retries=3
)
client = OpenAI(
api_key=API_KEY,
base_url=BASE_URL,
http_client=httpx.Client(transport=transport)
)
4. 토큰 계산 오류 및 부정확한 비용
# ❌ 문제: 응답의 usage 정보가 None인 경우
✅ 해결 방법
def safe_extract_usage(response, fallback_prompt_tokens: int = 0, fallback_completion_tokens: int = 0):
"""안전하게 usage 정보 추출"""
if response.usage is None:
logger.warning("usage 정보 없음. 추정치 사용.")
# Claude API의 경우 토큰 수가 없을 수 있음
return {
"prompt_tokens": fallback_prompt_tokens,
"completion_tokens": fallback_completion_tokens,
"total_tokens": fallback_prompt_tokens + fallback_completion_tokens,
"estimated": True
}
return {
"prompt_tokens": response.usage.prompt_tokens,
"completion_tokens": response.usage.completion_tokens,
"total_tokens": response.usage.total_tokens,
"estimated": False
}
모델별 토큰 추정 함수 (Claude 등에서 사용)
def estimate_tokens_from_text(text: str, encoding_name: str = "cl100k_base") -> int:
"""대략적인 토큰 수 추정 (글자 수 기반)"""
# 영어: ~4자 = 1토큰
# 한국어: ~2자 = 1토큰 (대략적)
char_count = len(text)
# 한국어 비율 추정
korean_ratio = sum(1 for c in text if '\uAC00' <= c <= '\uD7AF') / max(char_count, 1)
english_estimate = char_count * (1 - korean_ratio) / 4
korean_estimate = char_count * korean_ratio / 2
return int(english_estimate + korean_estimate)
5. 데이터베이스 잠금 및 동시성 문제
# ❌ 오류 메시지
sqlite3.OperationalError: database is locked
✅ 해결 방법
import sqlite3
import threading
from contextlib import contextmanager
class ThreadSafeLogger(AICallLogger):
def __init__(self, db_path: str = "ai_logs.db"):
self._lock = threading.Lock()
super().__init__(db_path)
@contextmanager
def _get_connection(self):
with self._lock:
conn = sqlite3.connect(
self.db_path,
timeout=30.0, # 잠금 대기 시간
check_same_thread=False # 스레드 간 공유 허용
)
conn.execute("PRAGMA journal_mode=WAL") # WAL 모드로 전환
try:
yield conn
finally:
conn.close()
def batch_log(self, log_entries: List[APICallLog]):
"""배치 삽입으로 성능 향상"""
with self._lock:
conn = sqlite3.connect(self.db_path, timeout=30.0)
try:
cursor = conn.cursor()
data = [
(
entry.timestamp, entry.model, entry.prompt_tokens,
entry.completion_tokens, entry.total_tokens,
entry.prompt_cost, entry.completion_cost, entry.total_cost,
entry.latency_ms, entry.status, entry.error_message,
entry.request_id, entry.user_id, entry