ไฝ ๆ ๆณๆงๅถไฝ ๆฒกๆ้ข็ฎ็ไธ่ฅฟใToken ้ข็ฎ = ๆๆฌ็ๅคฉ่ฑๆฟโโๆ็จๆทใๆไผ่ฏใๆๅคฉ่ฎพ้๏ผ่ถ ้ข็ฎ่ชๅจ้็บงใ
้ข็ฎๅฑ็บง๏ผไป็ปๅฐ็ฒ๏ผ๏ผ
่ฏทๆฑ็บง โ ไผ่ฏ็บง โ ็จๆท็บง โ ็งๆท็บง โ ๅ
จๅฑ็บง
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ๅ
จๅฑ้ข็ฎ: $5000/ๆ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ ็งๆทA: $2000/ๆ โ ็งๆทB: $1000/ๆ โ ... โ โ
โ โ โโโโโโโโโโโโโโโโ โ โโโโโโโโโโโโโโโโ โ โ
โ โ โ็จๆท1: $50/ๆ โ โ โ็จๆท1: $30/ๆ โ โ โ
โ โ โ โโโโโโโโโโโโ โ โ โ โโโโโโโโโโโโ โ โ โ
โ โ โ โไผ่ฏ1: โ โ โ โ โไผ่ฏ1: โ โ โ โ
โ โ โ โ1000tok/่ฝฎโ โ โ โ โ800tok/่ฝฎ โ โ โ โ
โ โ โ โโโโโโโโโโโโ โ โ โ โโโโโโโโโโโโ โ โ โ
โ โ โโโโโโโโโโโโโโโโ โ โโโโโโโโโโโโโโโโ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
from dataclasses import dataclass, field
from datetime import datetime, timedelta
from enum import Enum
import time
class BudgetLevel(Enum):
REQUEST = "request" # ๅๆฌก่ฏทๆฑ
SESSION = "session" # ๅไธชไผ่ฏ
USER_DAILY = "user_daily"
USER_MONTHLY = "user_monthly"
TENANT_MONTHLY = "tenant_monthly"
GLOBAL_MONTHLY = "global_monthly"
class DegradeAction(Enum):
CHEAPER_MODEL = "cheaper_model" # ้็บงๅฐๆดไพฟๅฎ็ๆจกๅ
REDUCE_CONTEXT = "reduce_context" # ๅๅฐไธไธๆ้ฟๅบฆ
QUEUE = "queue" # ๆ้็ญๅพ
REJECT = "reject" # ๆ็ป่ฏทๆฑ
NOTIFY = "notify" # ไป
้็ฅ๏ผไธ้ๅถ
@dataclass
class BudgetLimit:
level: BudgetLevel
max_tokens: int
max_cost: float # USD
degrade_action: DegradeAction
warning_threshold: float = 0.8 # 80% ๆถๅ่ญฆ
@dataclass
class BudgetUsage:
level: BudgetLevel
key: str # ๆ ่ฏ๏ผuser_id, session_id ็ญ๏ผ
tokens_used: int = 0
cost_used: float = 0.0
period_start: datetime = None
period_end: datetime = None
class TokenBudgetManager:
"""Token ้ข็ฎ็ฎก็ๅจ"""
# ้ป่ฎค้ข็ฎ็ญ็ฅ
DEFAULT_LIMITS = {
BudgetLevel.REQUEST: BudgetLimit(
level=BudgetLevel.REQUEST,
max_tokens=8000,
max_cost=0.50,
degrade_action=DegradeAction.REDUCE_CONTEXT
),
BudgetLevel.SESSION: BudgetLimit(
level=BudgetLevel.SESSION,
max_tokens=100000,
max_cost=5.00,
degrade_action=DegradeAction.CHEAPER_MODEL
),
BudgetLevel.USER_DAILY: BudgetLimit(
level=BudgetLevel.USER_DAILY,
max_tokens=500000,
max_cost=10.00,
degrade_action=DegradeAction.CHEAPER_MODEL
),
BudgetLevel.USER_MONTHLY: BudgetLimit(
level=BudgetLevel.USER_MONTHLY,
max_tokens=10000000,
max_cost=50.00,
degrade_action=DegradeAction.REJECT
),
}
def __init__(self, redis_client, custom_limits: dict = None):
self.redis = redis_client
self.limits = custom_limits or self.DEFAULT_LIMITS
self.alerts = []
async def check_budget(self, user_id: str, session_id: str,
estimated_tokens: int = 0) -> dict:
"""ๆฃๆฅ้ข็ฎๆฏๅฆๅ
่ถณ"""
checks = {}
# ๆฃๆฅๅๅฑ็บง้ข็ฎ
for level in [BudgetLevel.REQUEST, BudgetLevel.SESSION,
BudgetLevel.USER_DAILY, BudgetLevel.USER_MONTHLY]:
limit = self.limits[level]
usage = await self._get_usage(level, user_id, session_id)
# ๆฃๆฅ token ้ข็ฎ
token_pct = (usage.tokens_used + estimated_tokens) / limit.max_tokens
# ๆฃๆฅๆๆฌ้ข็ฎ
cost_pct = usage.cost_used / limit.max_cost if limit.max_cost > 0 else 0
max_pct = max(token_pct, cost_pct)
over_budget = max_pct > 1.0
near_limit = max_pct > limit.warning_threshold
checks[level.value] = {
'over_budget': over_budget,
'usage_pct': max_pct,
'near_limit': near_limit,
'degrade_action': limit.degrade_action.value if over_budget else None
}
# ๅ่ญฆ
if near_limit and not over_budget:
await self._send_alert(level, user_id, max_pct)
# ๆฑๆปๅณ็ญ
over_levels = [k for k, v in checks.items() if v['over_budget']]
if over_levels:
# ๅๆไธฅๆ ผ็้็บง็ญ็ฅ
most_restrictive = self._most_restrictive_action(over_levels)
return {
'allowed': most_restrictive != DegradeAction.REJECT,
'action': most_restrictive.value,
'checks': checks
}
return {'allowed': True, 'action': None, 'checks': checks}
async def record_usage(self, user_id: str, session_id: str,
tokens_used: int, cost: float):
"""่ฎฐๅฝไฝฟ็จ้"""
for level in [BudgetLevel.REQUEST, BudgetLevel.SESSION,
BudgetLevel.USER_DAILY, BudgetLevel.USER_MONTHLY]:
key = self._usage_key(level, user_id, session_id)
# Redis INCRBY + ่ฎพ็ฝฎ่ฟๆๆถ้ด
pipe = self.redis.pipeline()
pipe.incrby(f"{key}:tokens", tokens_used)
pipe.incrbyfloat(f"{key}:cost", cost)
# ่ฎพ็ฝฎ่ฟๆๆถ้ด
if level == BudgetLevel.USER_DAILY:
pipe.expire(f"{key}:tokens", 86400)
pipe.expire(f"{key}:cost", 86400)
elif level == BudgetLevel.USER_MONTHLY:
pipe.expire(f"{key}:tokens", 30*86400)
pipe.expire(f"{key}:cost", 30*86400)
await pipe.execute()
async def _get_usage(self, level: BudgetLevel, user_id: str,
session_id: str) -> BudgetUsage:
"""่ทๅๅฝๅไฝฟ็จ้"""
key = self._usage_key(level, user_id, session_id)
tokens = int(await self.redis.get(f"{key}:tokens") or 0)
cost = float(await self.redis.get(f"{key}:cost") or 0)
return BudgetUsage(
level=level,
key=key,
tokens_used=tokens,
cost_used=cost
)
def _usage_key(self, level: BudgetLevel, user_id: str,
session_id: str) -> str:
"""็ๆ Redis key"""
today = datetime.now().strftime('%Y-%m-%d')
month = datetime.now().strftime('%Y-%m')
if level == BudgetLevel.REQUEST:
return f"budget:rq:{session_id}"
elif level == BudgetLevel.SESSION:
return f"budget:ss:{session_id}"
elif level == BudgetLevel.USER_DAILY:
return f"budget:ud:{user_id}:{today}"
elif level == BudgetLevel.USER_MONTHLY:
return f"budget:um:{user_id}:{month}"
def _most_restrictive_action(self, levels: list) -> DegradeAction:
"""้ๆฉๆไธฅๆ ผ็้็บง็ญ็ฅ"""
priority = [DegradeAction.REJECT, DegradeAction.CHEAPER_MODEL,
DegradeAction.REDUCE_CONTEXT, DegradeAction.QUEUE]
for action in priority:
for level in levels:
limit = self.limits.get(BudgetLevel(level))
if limit and limit.degrade_action == action:
return action
return DegradeAction.NOTIFY
async def _send_alert(self, level: BudgetLevel, user_id: str, pct: float):
"""ๅ้้ข็ฎๅ่ญฆ"""
alert = {
'level': level.value,
'user_id': user_id,
'usage_pct': pct,
'timestamp': datetime.now().isoformat()
}
self.alerts.append(alert)
# ๅฎ้
ๅฎ็ฐ๏ผๅๅฐ Slack/Email/Webhook
print(f"โ ๏ธ ้ข็ฎๅ่ญฆ: {level.value} - ็จๆท {user_id} ๅทฒไฝฟ็จ {pct*100:.0f}%")
class DegradeStrategy:
"""้็บง็ญ็ฅ"""
# ๆจกๅ้็บง้พ
MODEL_DEGRADE_CHAIN = {
"claude-opus-4-20250514": "claude-sonnet-4-20250514",
"claude-sonnet-4-20250514": "claude-3-5-haiku-20241022",
"gpt-5.5": "gpt-5.4",
"gpt-5.4": "gpt-5.4-mini",
"gpt-4o": "gpt-4o-mini",
"gpt-4o-mini": "deepseek-chat",
}
def get_cheaper_model(self, current_model: str) -> str:
"""่ทๅไธไธ็บงๆดไพฟๅฎ็ๆจกๅ"""
return self.MODEL_DEGRADE_CHAIN.get(current_model, "deepseek-chat")
def reduce_context(self, messages: list, max_tokens: int) -> list:
"""ๅๅฐไธไธๆ้ฟๅบฆ"""
total = sum(len(m['content']) for m in messages)
if total <= max_tokens * 4: # ็ฒไผฐ
return messages
# ไฟ็็ณป็ปๆถๆฏ + ๆ่ฟ็ๅฏน่ฏ
system_msgs = [m for m in messages if m['role'] == 'system']
other_msgs = [m for m in messages if m['role'] != 'system']
# ไปๆงๅฐๆฐๆชๆญ
budget = max_tokens * 4
system_size = sum(len(m['content']) for m in system_msgs)
remaining = budget - system_size
kept = []
for msg in reversed(other_msgs):
if remaining <= 0:
break
kept.append(msg)
remaining -= len(msg['content'])
return system_msgs + list(reversed(kept))
# ้ๆๅฐ่ฏทๆฑๆต็จ
degrade = DegradeStrategy()
async def chat_with_budget(user_id: str, session_id: str,
messages: list, model: str = "gpt-4o"):
# 1. ๆฃๆฅ้ข็ฎ
budget_check = await budget_manager.check_budget(
user_id, session_id, estimated_tokens=1000
)
current_model = model
current_messages = messages
if not budget_check['allowed']:
return {"error": "ๅทฒ่ถ
ๅบไฝฟ็จ้ข็ฎ๏ผ่ฏทๆๅคฉๅ่ฏ"}
if budget_check['action'] == 'cheaper_model':
current_model = degrade.get_cheaper_model(model)
elif budget_check['action'] == 'reduce_context':
current_messages = degrade.reduce_context(messages, max_tokens=4000)
# 2. ่ฐ็จ LLM
response = await call_llm(model=current_model, messages=current_messages)
# 3. ่ฎฐๅฝไฝฟ็จ้
tokens = response.usage.total_tokens
cost = calculate_cost(current_model, response.usage)
await budget_manager.record_usage(user_id, session_id, tokens, cost)
return {
"response": response.choices[0].message.content,
"model_used": current_model,
"degraded": current_model != model,
"tokens_used": tokens,
"cost": cost
}
| ็จๆท็ฑปๅ | ๆฏๆฅ Token | ๆฏๆฅๆๆฌ | ๆจกๅ | ้็บง็ญ็ฅ |
|---|---|---|---|---|
| ๅ ่ดน็จๆท | 50K | $0.50 | GPT-4o-mini | ่ถ ้ๆ็ป |
| ๅบ็กไป่ดน | 500K | $5.00 | GPT-4o | ้็บงๅฐ mini |
| Pro ไป่ดน | 2M | $20.00 | GPT-4o/Claude | ้็บงๅฐ GPT-4o |
| ไผไธ็จๆท | ่ชๅฎไน | ่ชๅฎไน | ่ชๅฎไน | ้็ฅ็ฎก็ๅ |
class CostAlertManager:
"""ๆๆฌๆฅ่ญฆ็ฎก็"""
ALERT_LEVELS = [
{'threshold': 0.5, 'level': 'info', 'message': 'ๅทฒไฝฟ็จ50%้ข็ฎ'},
{'threshold': 0.8, 'level': 'warning', 'message': 'ๅทฒไฝฟ็จ80%้ข็ฎ'},
{'threshold': 0.95, 'level': 'critical', 'message': 'ๅทฒไฝฟ็จ95%้ข็ฎ'},
{'threshold': 1.0, 'level': 'emergency', 'message': 'ๅทฒ่ถ
ๅบ้ข็ฎ'},
]
async def check_and_alert(self, scope: str, usage_pct: float):
"""ๆฃๆฅๅนถๅ้ๆฅ่ญฆ"""
for alert in self.ALERT_LEVELS:
if usage_pct >= alert['threshold']:
await self._send_alert(scope, alert, usage_pct)
async def _send_alert(self, scope: str, alert: dict, pct: float):
"""ๅ้ๆฅ่ญฆ๏ผๅคๆธ ้๏ผ"""
message = f"[{alert['level'].upper()}] {scope}: {alert['message']} ({pct*100:.1f}%)"
# Slack ๆฅ่ญฆ
if alert['level'] in ['critical', 'emergency']:
await send_slack("#cost-alerts", message)
# Email ๆฅ่ญฆ
if alert['level'] == 'emergency':
await send_email("admin@company.com",
"๐จ LLM ๆๆฌ่ถ
้ข็ฎ", message)
# ่ชๅจ้็บง
if alert['level'] == 'emergency':
await self._auto_degrade(scope)
async def _auto_degrade(self, scope: str):
"""่ชๅจ้็บงโโ็ดงๆฅไฟๆค"""
# ๅ
จๅฑ้็บงๅฐๆไพฟๅฎ็ๆจกๅ
await set_global_default_model("deepseek-chat")
await send_slack("#cost-alerts",
f"๐จ ๅทฒ่ชๅจ้็บงๅฐ deepseek-chat ไปฅๆงๅถๆๆฌ")
โ ไธไธ็ฏ๏ผBatch API | ไธไธ็ฏ๏ผEmbedding ๆๆฌ โ | ่ฟๅ้ฆ้กต