【Claude】成本控制与用量监控实战 — 已解决
·
【Claude】成本控制与用量监控实战 — 已解决
适用版本:Claude Code v1.0.x 及以上
受影响场景:API 费用管理、Token 消耗优化、团队用量追踪、预算控制
阅读时长:约 25 分钟
目录
1. 问题现象
1.1 典型问题表现
问题一:API 费用突然飙升
# Anthropic Console 月账单
# 1月: $50 → 正常使用
# 2月: $500 → 10 倍增长!
# 不知道哪里的消耗暴增
问题二:单个会话消耗大量 Token
# 一个简单的代码审查任务
> 审查 src/auth.py
# Claude Code 读取了整个代码库 (50个文件)
# 消耗 200K input tokens
# 单次操作成本 $0.60
问题三:团队成员用量不透明
# 5人团队共享一个 API Key
# 月账单 $300
# 不知道谁用了多少
# 无法按人/项目分摊成本
问题四:缓存未命中导致重复消耗
# 每次对话都重新发送完整上下文
# 没有使用 prompt caching
# 相同的系统提示和上下文被重复计费
问题五:长会话 Token 累积
# 长时间会话 (50+ 轮)
# 早期对话不断被重新发送
# 每轮的 input tokens 递增
# 后期单轮消耗 100K+ tokens
2. 原理深挖:Token 计费模型
2.1 定价结构
┌──────────────────────────────────────────────────────────┐
│ Anthropic API 定价 (每百万 Token) │
├──────────────────────────────────────────────────────────┤
│ │
│ 模型 Input Output Cache Read Cache Write │
│ ─────────────────────────────────────────────────────── │
│ Claude Opus 4 $15 $75 $1.50 $18.75 │
│ Claude Sonnet 4 $3 $15 $0.30 $3.75 │
│ Claude Haiku 4 $0.25 $1.25 $0.025 $0.3125 │
│ │
│ 计费规则: │
│ - Input: 用户消息 + 系统提示 + 历史对话 + 工具结果 │
│ - Output: Claude 生成的文本和工具调用 │
│ - Cache Read: 从缓存读取的 input (90% 折扣) │
│ - Cache Write: 写入缓存的 input (1.25x 标准价) │
│ - 缓存 TTL: 5 分钟 │
│ │
│ 隐藏成本: │
│ - 工具调用结果计入 input │
│ - 大文件读取 = 大量 input tokens │
│ - 长对话历史 = 递增 input │
│ - 错误重试 = 重复消耗 │
│ │
└──────────────────────────────────────────────────────────┘
2.2 Token 计算规则
Token 估算:
英文: 1 token ≈ 4 字符 ≈ 0.75 单词
中文: 1 token ≈ 1-2 字符 (中文 token 密度高)
代码: 1 token ≈ 3-4 字符
示例:
"Hello World" → ~3 tokens
"你好世界" → ~4-6 tokens
"def hello(): print('hi')" → ~12 tokens
Claude Code 每轮消耗:
Input = 系统提示(~500 tokens)
+ CLAUDE.md(~200-1000 tokens)
+ 历史对话(递增)
+ 工具调用结果
Output = Claude 的回复 + 工具调用 JSON
2.3 长会话成本模型
会话成本递增模型 (无缓存):
第 1 轮: Input = 1K, Output = 0.5K → $0.00375
第 2 轮: Input = 2K, Output = 0.5K → $0.00750
第 3 轮: Input = 3K, Output = 0.5K → $0.01125
...
第 N 轮: Input = N*1K, Output = 0.5K → $0.00375*N
总成本 = Σ(0.00375 * N) for N=1..50
= 0.00375 * (1+2+...+50)
= 0.00375 * 1275
= $4.78 (50 轮对话)
带缓存优化后:
第 1 轮: Input = 1K (cache write), Output = 0.5K
第 2 轮: Input = 0.3K (cache read) + 1K (new), Output = 0.5K
第 3 轮: Input = 0.6K (cache read) + 1K (new), Output = 0.5K
...
总成本 ≈ $1.50 (约 70% 节省)
2.4 Anthropic Console 用量统计
console.anthropic.com → Usage 页面:
按时间: 日/周/月用量趋势
按模型: 各模型的 Token 消耗
按项目: 不同 API Key 的用量
按类型: Input/Output/Cache 分布
限制:
- 无法按用户区分 (共享 API Key)
- 无法按会话区分
- 无实时告警
- 只有日级粒度
3. 根因分析:成本失控的六大根因
3.1 根因一:未使用 Prompt Caching
每次请求都重新发送完整上下文,没有利用缓存,input 成本高出数倍。
3.2 根因二:长会话不截断
会话越长,每轮的 input tokens 越多(历史对话累积),成本呈二次增长。
3.3 根因三:大文件全量读取
Claude Code 读取大文件时消耗大量 input tokens,特别是 node_modules、lock 文件等。
3.4 根因四:模型选择不当
简单任务用 Opus($15/M),应该用 Haiku($0.25/M)或 Sonnet($3/M)。
3.5 根因五:无用量监控
没有实时监控 Token 消耗,成本在不知不觉中累积。
3.6 根因六:团队共享 Key
多人共用一个 API Key,无法按人/项目追踪和限制用量。
4. 多方案解决:从监控到优化
4.1 方案一:实时成本监控 Hook
#!/usr/bin/env python3
# .claude/hooks/cost-monitor.py — 实时成本监控
import json
import sys
import os
from datetime import datetime
from pathlib import Path
COST_DIR = Path(".claude/costs")
COST_DIR.mkdir(parents=True, exist_ok=True)
PRICING = {
"claude-opus-4-20250514": {"input": 15.0, "output": 75.0},
"claude-sonnet-4-20250514": {"input": 3.0, "output": 15.0},
"claude-haiku-4-20250422": {"input": 0.25, "output": 1.25},
}
# 预算阈值 (美元)
DAILY_BUDGET = float(os.environ.get("CLAUDE_DAILY_BUDGET", "10.0"))
MONTHLY_BUDGET = float(os.environ.get("CLAUDE_MONTHLY_BUDGET", "200.0"))
def calculate_cost(model, usage):
"""计算成本"""
pricing = PRICING.get(model, PRICING["claude-sonnet-4-20250514"])
input_cost = usage.get("input_tokens", 0) * pricing["input"] / 1_000_000
output_cost = usage.get("output_tokens", 0) * pricing["output"] / 1_000_000
cache_read = usage.get("cache_read_input_tokens", 0) * pricing["input"] * 0.1 / 1_000_000
cache_write = usage.get("cache_creation_input_tokens", 0) * pricing["input"] * 1.25 / 1_000_000
return round(input_cost + output_cost + cache_read + cache_write, 6)
def get_daily_total():
"""获取今日总成本"""
today = datetime.utcnow().strftime("%Y-%m-%d")
log_file = COST_DIR / f"cost-{today}.jsonl"
total = 0.0
if log_file.exists():
with open(log_file) as f:
for line in f:
try:
data = json.loads(line)
total += data.get("cost_usd", 0)
except:
pass
return total
def get_monthly_total():
"""获取本月总成本"""
month = datetime.utcnow().strftime("%Y-%m")
total = 0.0
for log_file in COST_DIR.glob(f"cost-{month}-*.jsonl"):
with open(log_file) as f:
for line in f:
try:
data = json.loads(line)
total += data.get("cost_usd", 0)
except:
pass
return total
# Hook 入口
try:
hook_input = json.load(sys.stdin)
if "usage" in hook_input:
model = hook_input.get("model", "claude-sonnet-4-20250514")
usage = hook_input["usage"]
cost = calculate_cost(model, usage)
# 记录
entry = {
"timestamp": datetime.utcnow().isoformat() + "Z",
"model": model,
"input_tokens": usage.get("input_tokens", 0),
"output_tokens": usage.get("output_tokens", 0),
"cost_usd": cost,
"project": os.path.basename(os.getcwd()),
"session": os.environ.get("CLAUDE_SESSION_ID", "unknown")
}
today = datetime.utcnow().strftime("%Y-%m-%d")
with open(COST_DIR / f"cost-{today}.jsonl", "a") as f:
f.write(json.dumps(entry) + "\n")
# 预算检查
daily_total = get_daily_total()
monthly_total = get_monthly_total()
if daily_total > DAILY_BUDGET:
print(f"⚠ 日预算告警: ${daily_total:.2f} / ${DAILY_BUDGET:.2f}", file=sys.stderr)
if monthly_total > MONTHLY_BUDGET:
print(f"⚠ 月预算告警: ${monthly_total:.2f} / ${MONTHLY_BUDGET:.2f}", file=sys.stderr)
# 实时显示
print(f"[Cost] ${cost:.4f} | 日: ${daily_total:.2f}/{DAILY_BUDGET} | 月: ${monthly_total:.2f}/{MONTHLY_BUDGET}", file=sys.stderr)
except Exception as e:
print(f"Cost monitor error: {e}", file=sys.stderr)
sys.exit(0)
4.2 方案二:Prompt Caching 配置
"""
Prompt Caching 优化
通过缓存系统提示和上下文,减少重复 input 成本
"""
import anthropic
client = anthropic.Anthropic(api_key="sk-ant-xxx")
# 缓存系统提示 (长期不变的部分)
SYSTEM_PROMPT = """你是一个代码助手。
项目规则:
- 使用 TypeScript
- 代码风格遵循 ESLint 配置
- 测试使用 Jest
- 文档使用 JSDoc
项目结构:
- src/ → 源代码
- tests/ → 测试
- docs/ → 文档"""
# 带 cache_control 的请求
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=4096,
system=[
{
"type": "text",
"text": SYSTEM_PROMPT,
"cache_control": {"type": "ephemeral"} # 缓存 5 分钟
}
],
messages=[
{"role": "user", "content": "修复 src/auth.ts 的 bug"}
]
)
# 第一次请求: cache_creation_input_tokens > 0 (写入缓存)
# 后续 5 分钟内的请求: cache_read_input_tokens > 0 (读取缓存, 90% 折扣)
print(f"Input: {response.usage.input_tokens}")
print(f"Output: {response.usage.output_tokens}")
print(f"Cache Read: {response.usage.cache_read_input_tokens}")
print(f"Cache Write: {response.usage.cache_creation_input_tokens}")
# 多级缓存策略
class CachedConversation:
"""带多级缓存的对话"""
def __init__(self, client, system_prompt, model="claude-sonnet-4-20250514"):
self.client = client
self.model = model
self.messages = []
# 系统提示缓存
self.system = [
{
"type": "text",
"text": system_prompt,
"cache_control": {"type": "ephemeral"}
}
]
# 对话历史缓存 (在历史达到一定长度时设置)
self._cache_breakpoint = 4 # 每 4 轮设置一个缓存断点
def chat(self, user_message):
"""对话"""
self.messages.append({"role": "user", "content": user_message})
# 在历史消息中设置缓存断点
# 最后一个消息不缓存(因为可能会变)
system_with_cache = list(self.system)
# 如果历史较长,在历史中设置缓存

if len(self.messages) > self._cache_breakpoint * 2:
# 在倒数第 4 轮设置缓存断点
cache_idx = len(self.messages) - self._cache_breakpoint * 2
# 需要将消息转为 content block 格式并设置 cache_control
pass # 实际实现需要更复杂的消息格式处理
response = self.client.messages.create(
model=self.model,
max_tokens=4096,
system=system_with_cache,
messages=self.messages
)
self.messages.append({"role": "assistant", "content": response.content})
# 报告缓存命中
usage = response.usage
cache_hit = usage.cache_read_input_tokens > 0
print(f" Cache: {'✓ 命中' if cache_hit else '✗ 未命中'} "
f"(read: {usage.cache_read_input_tokens}, write: {usage.cache_creation_input_tokens})")
return response.content[0].text
4.3 方案三:模型分级策略
"""
模型分级策略: 根据任务复杂度选择模型
"""
MODEL_TIERS = {
"simple": {
"model": "claude-haiku-4-20250422",
"cost_per_million": {"input": 0.25, "output": 1.25},
"tasks": ["简单问答", "格式转换", "简单搜索", "变量重命名"]
},
"medium": {
"model": "claude-sonnet-4-20250514",
"cost_per_million": {"input": 3.0, "output": 15.0},
"tasks": ["代码编写", "Bug 修复", "代码审查", "测试生成"]
},
"complex": {
"model": "claude-opus-4-20250514",
"cost_per_million": {"input": 15.0, "output": 75.0},
"tasks": ["架构设计", "复杂重构", "安全分析", "算法优化"]
}
}
# 任务分类 → 模型选择
TASK_CLASSIFICATION = {
# 简单任务 → Haiku (最低成本)
"解释这行代码": "simple",
"变量重命名": "simple",
"格式化代码": "simple",
"查找 import": "simple",
"简单的语法问题": "simple",
# 中等任务 → Sonnet (性价比)
"写一个函数": "medium",
"修复 bug": "medium",
"添加测试": "medium",
"代码审查": "medium",
"重构函数": "medium",
# 复杂任务 → Opus (最高质量)
"系统架构设计": "complex",
"大规模重构": "complex",
"安全漏洞分析": "complex",
"算法优化": "complex",
"复杂并发问题": "complex",
}
def select_model(task_description):
"""根据任务描述选择模型"""
task_lower = task_description.lower()
for pattern, tier in TASK_CLASSIFICATION.items():
if pattern in task_lower:
return MODEL_TIERS[tier]["model"]
# 默认中等
return MODEL_TIERS["medium"]["model"]
# Claude Code 配置
# .claude/settings.json
"""
{
"model": "claude-sonnet-4-20250514", // 主模型 (中等任务)
"smallModel": "claude-haiku-4-20250422", // 小模型 (简单任务)
}
"""
4.4 方案四:上下文压缩
"""
上下文压缩: 减少长会话的 input token 消耗
"""
import anthropic
class ContextCompressor:
"""对话上下文压缩器"""
def __init__(self, client, model="claude-haiku-4-20250422"):
self.client = client
self.model = model # 用便宜模型做压缩
def compress_history(self, messages, keep_recent=4):
"""
压缩历史对话
保留最近 N 轮原始对话,之前的对话用摘要替代
"""
if len(messages) <= keep_recent * 2:
return messages # 不需要压缩
# 分割: 需要压缩的 + 保留的
to_compress = messages[:-keep_recent * 2]
to_keep = messages[-keep_recent * 2:]
# 用 Haiku 生成摘要
summary_prompt = "请总结以下对话的关键信息,保留: 讨论的问题、做出的决定、修改的文件、关键代码片段。\n\n"
for msg in to_compress:
role = msg["role"]
content = msg["content"] if isinstance(msg["content"], str) else str(msg["content"])
summary_prompt += f"[{role}]: {content[:500]}\n"
response = self.client.messages.create(
model=self.model,
max_tokens=1000,
messages=[{"role": "user", "content": summary_prompt}]
)
summary = response.content[0].text
# 构建压缩后的消息列表
compressed = [
{"role": "user", "content": f"[之前对话的摘要]\n{summary}"},
{"role": "assistant", "content": "了解,我记住了之前的对话内容。请继续。"}
] + to_keep
# 成本报告
original_tokens = sum(len(str(m.get("content", ""))) // 4 for m in to_compress)
compressed_tokens = len(summary) // 4
savings = max(0, original_tokens - compressed_tokens)
print(f" 上下文压缩: {original_tokens} → {compressed_tokens} tokens (节省 {savings})")
return compressed
# 使用
client = anthropic.Anthropic(api_key="sk-ant-xxx")
compressor = ContextCompressor(client)
# 长对话压缩
# messages = [很多轮对话...]
# messages = compressor.compress_history(messages, keep_recent=4)
4.5 方案五:团队用量追踪
#!/usr/bin/env python3
"""
团队用量追踪系统
按用户、项目、会话维度追踪 API 成本
"""
import json
import os
from datetime import datetime, timedelta
from pathlib import Path
from collections import defaultdict
class TeamCostTracker:
"""团队成本追踪器"""
def __init__(self):
self.cost_dir = Path(os.environ.get("CLAUDE_COST_DIR", ".claude/costs"))
def generate_team_report(self, days=30):
"""生成团队报告"""
stats = defaultdict(lambda: {
"total_cost": 0.0,
"total_input": 0,
"total_output": 0,
"api_calls": 0,
"by_model": defaultdict(float),
"by_project": defaultdict(float),
"by_day": defaultdict(float)
})
# 加载日志
for i in range(days):
date = (datetime.utcnow() - timedelta(days=i)).strftime("%Y-%m-%d")
log_file = self.cost_dir / f"cost-{date}.jsonl"
if not log_file.exists():
continue
with open(log_file) as f:
for line in f:
try:
entry = json.loads(line)
# 按 session (用户代理) 统计
session = entry.get("session", "unknown")
stats[session]["total_cost"] += entry.get("cost_usd", 0)
stats[session]["total_input"] += entry.get("input_tokens", 0)
stats[session]["total_output"] += entry.get("output_tokens", 0)
stats[session]["api_calls"] += 1
stats[session]["by_model"][entry.get("model", "unknown")] += entry.get("cost_usd", 0)
stats[session]["by_project"][entry.get("project", "unknown")] += entry.get("cost_usd", 0)
stats[session]["by_day"][date] += entry.get("cost_usd", 0)
except json.JSONDecodeError:
continue
# 打印报告
print(f"=== 团队成本报告 ({days} 天) ===\n")
grand_total = sum(s["total_cost"] for s in stats.values())
print(f"总成本: ${grand_total:.2f}")
print(f"总 API 调用: {sum(s['api_calls'] for s in stats.values())}")
print(f"总 Input: {sum(s['total_input'] for s in stats.values()):,} tokens")
print(f"总 Output: {sum(s['total_output'] for s in stats.values()):,} tokens")
print(f"\n--- 按会话 ---")
for session, data in sorted(stats.items(), key=lambda x: -x[1]["total_cost"]):
print(f"\n 会话: {session}")
print(f" 成本: ${data['total_cost']:.4f}")
print(f" 调用: {data['api_calls']}")
print(f" Input: {data['total_input']:,} / Output: {data['total_output']:,}")
if data["by_model"]:
print(f" 按模型:")
for model, cost in sorted(data["by_model"].items(), key=lambda x: -x[1]):
print(f" {model}: ${cost:.4f}")
if data["by_project"]:
print(f" 按项目:")
for project, cost in sorted(data["by_project"].items(), key=lambda x: -x[1]):
print(f" {project}: ${cost:.4f}")
# 日均趋势
print(f"\n--- 日均成本 ---")
all_days = set()
for data in stats.values():
all_days.update(data["by_day"].keys())
for date in sorted(all_days):
day_total = sum(data["by_day"].get(date, 0) for data in stats.values())
bar = "█" * int(day_total * 10) # 每美元 10 个字符
print(f" {date}: ${day_total:.2f} {bar}")
# 使用
tracker = TeamCostTracker()
tracker.generate_team_report(days=30)
4.6 方案六:文件读取优化
"""
文件读取 Token 优化
避免不必要的大文件全量读取
"""
import os
class FileReader:
"""优化的文件读取器"""
# 应该跳过的文件/目录
SKIP_PATTERNS = [
"node_modules", ".git", "dist", "build", "__pycache__",
"*.lock", "*.min.js", "*.min.css", "*.map",
"package-lock.json", "yarn.lock", "pnpm-lock.yaml",
"go.sum", "Cargo.lock", "Gemfile.lock",
]
# 大文件阈值 (行数)
MAX_LINES = 500
# 大文件阈值 (字节)
MAX_SIZE = 50_000 # 50KB
@classmethod
def should_read(cls, filepath):
"""判断是否应该读取文件"""
filepath = str(filepath)
# 检查跳过模式
for pattern in cls.SKIP_PATTERNS:
if pattern in filepath:
return False, f"跳过: 匹配 {pattern}"
# 检查文件大小
if os.path.exists(filepath):
size = os.path.getsize(filepath)
if size > cls.MAX_SIZE:
return False, f"文件过大: {size} bytes (> {cls.MAX_SIZE})"
return True, "OK"
@classmethod
def read_optimized(cls, filepath, max_lines=None):
"""优化的文件读取"""
should, reason = cls.should_read(filepath)
if not should:
return None, reason
max_lines = max_lines or cls.MAX_LINES
with open(filepath, "r", errors="replace") as f:
lines = []
for i, line in enumerate(f):
if i >= max_lines:
lines.append(f"\n... (截断,共 {i+1}+ 行,仅显示前 {max_lines} 行)")
break
lines.append(line)
content = "".join(lines)
estimated_tokens = len(content) // 4
return content, f"读取 {len(lines)} 行, ~{estimated_tokens} tokens"
# CLAUDE.md 中配置读取策略
"""
# 文件读取策略
## 跳过的文件
- node_modules/
- *.lock 文件
- *.min.js / *.min.css
- dist/ / build/
## 大文件处理
- 超过 500 行的文件只读取相关部分
- 超过 50KB 的文件先摘要再选择性读取
- 使用 grep/search 定位再读取
## 优先级
- 先用 search_content 定位
- 再用 read_file offset/limit 精确读取
- 避免全量读取大文件
"""
4.7 方案七:多 API Key 管理
"""
多 API Key 管理: 按项目/团队分配不同的 Key
"""
import os
import json
from pathlib import Path
class APIKeyManager:
"""多 API Key 管理器"""
def __init__(self):
self.keys_file = Path.home() / ".claude" / "api-keys.json"
self.keys = self._load_keys()
def _load_keys(self):
"""加载 API Key 配置"""
if not self.keys_file.exists():
return {}
with open(self.keys_file) as f:
return json.load(f)
def get_key_for_project(self, project_name):
"""获取项目对应的 API Key"""
project_config = self.keys.get("projects", {}).get(project_name)
if project_config:
key_env = project_config.get("env_var")
if key_env:
return os.environ.get(key_env)
# 返回默认 Key
return os.environ.get("ANTHROPIC_API_KEY")
def get_budget_for_project(self, project_name):
"""获取项目预算"""
project_config = self.keys.get("projects", {}).get(project_name, {})
return project_config.get("daily_budget", 10.0)
# api-keys.json 示例
"""
{
"projects": {
"my-app": {
"env_var": "ANTHROPIC_API_KEY_APP",
"daily_budget": 5.0,
"monthly_budget": 100.0
},
"internal-tools": {
"env_var": "ANTHROPIC_API_KEY_TOOLS",
"daily_budget": 2.0,
"monthly_budget": 50.0
},
"research": {
"env_var": "ANTHROPIC_API_KEY_RESEARCH",
"daily_budget": 20.0,
"monthly_budget": 400.0
}
},
"default": {
"env_var": "ANTHROPIC_API_KEY",
"daily_budget": 10.0,
"monthly_budget": 200.0
}
}
"""
# .zshrc 中配置多个 Key
"""
export ANTHROPIC_API_KEY="sk-ant-xxx-default"
export ANTHROPIC_API_KEY_APP="sk-ant-xxx-app"
export ANTHROPIC_API_KEY_TOOLS="sk-ant-xxx-tools"
export ANTHROPIC_API_KEY_RESEARCH="sk-ant-xxx-research"
"""
5. 验证回归:成本控制验证
5.1 成本验证脚本
#!/bin/bash
# verify-cost-control.sh — 成本控制验证
echo "=== 成本控制验证 ==="
# 1. 检查成本日志
if [ -d ".claude/costs" ]; then
TODAY=$(date -u +%Y-%m-%d)
LOG_FILE=".claude/costs/cost-${TODY}.jsonl"
if [ -f "$LOG_FILE" ]; then
TODAY_COST=$(python3 -c "
import json
total = 0
with open('$LOG_FILE') as f:
for line in f:
try:
data = json.loads(line)
total += data.get('cost_usd', 0)
except:
pass
print(f'{total:.4f}')
" 2>/dev/null)
echo "今日成本: \$$TODAY_COST"
fi
fi
# 2. 检查预算配置
DAILY_BUDGET=${CLAUDE_DAILY_BUDGET:-"未设置"}
echo "日预算: $DAILY_BUDGET"
# 3. 检查模型配置
if [ -f ".claude/settings.json" ]; then
python3 -c "
import json
with open('.claude/settings.json') as f:
data = json.load(f)
print(f\"主模型: {data.get('model', '未设置')}\")
print(f\"小模型: {data.get('smallModel', '未设置')}\")
" 2>/dev/null
fi
echo ""
echo "=== 验证完成 ==="
5.2 验证清单
| # | 验证项 | 预期 | 方法 |
|---|---|---|---|
| 1 | 成本 Hook | 已配置 | settings.json |
| 2 | 成本日志 | 有记录 | 检查 .claude/costs/ |
| 3 | 预算告警 | 功能正常 | 超阈值告警 |
| 4 | Prompt Caching | 命中率高 | cache_read > 0 |
| 5 | 模型分级 | 按任务选模型 | 配置检查 |
| 6 | 上下文压缩 | 长会话压缩 | 压缩工具 |
| 7 | 文件跳过 | 大文件不读 | 读取策略 |
| 8 | 多 Key | 按项目分配 | Key 管理器 |
6. 避坑最佳实践
6.1 成本控制原则
原则 1: 实时监控 — 用 Hook 记录每次 API 调用的成本
原则 2: Prompt Caching — 缓存系统提示和上下文
原则 3: 模型分级 — 简单任务用 Haiku,复杂用 Opus
原则 4: 上下文压缩 — 长会话定期压缩历史
原则 5: 文件优化 — 跳过大文件和无用文件
原则 6: 预算告警 — 设置日/月预算阈值
原则 7: 多 Key — 按项目/团队分配 API Key
原则 8: 定期审查 — 周度/月度成本分析
6.2 成本优化效果
| 优化措施 | 节省比例 | 实施难度 |
|---|---|---|
| Prompt Caching | 50-70% | 低 |
| 模型分级 (Haiku) | 80-90% | 低 |
| 上下文压缩 | 30-50% | 中 |
| 文件读取优化 | 20-40% | 低 |
| 长会话截断 | 40-60% | 低 |
| 多 Key 管理 | N/A | 中 |
6.3 常见陷阱
| # | 陷阱 | 后果 | 解决 |
|---|---|---|---|
| 1 | 无缓存 | 重复全价计费 | 开启 Prompt Caching |
| 2 | 简单任务用 Opus | 成本 60x | 模型分级 |
| 3 | 长会话不截断 | 二次增长 | 定期压缩 |
| 4 | 全量读大文件 | 浪费 input | 跳过/分页 |
| 5 | 无成本监控 | 不知花费 | Cost Hook |
| 6 | 共享 Key | 无法追踪 | 多 Key 管理 |
| 7 | 无预算限制 | 失控 | 预算告警 |
| 8 | 不审查账单 | 持续浪费 | 定期审查 |
7. 附录:成本速查表
7.1 模型定价对比
| 模型 | Input $/M | Output $/M | 适用场景 |
|---|---|---|---|
| Opus 4 | $15 | $75 | 架构/安全/复杂 |
| Sonnet 4 | $3 | $15 | 编码/审查/日常 |
| Haiku 4 | $0.25 | $1.25 | 简单/格式/搜索 |
7.2 成本优化优先级
| 优先级 | 措施 | 预期节省 |
|---|---|---|
| P0 | Prompt Caching | 50-70% |
| P0 | 模型分级 | 80%+ (简单任务) |
| P1 | 文件读取优化 | 20-40% |
| P1 | 长会话压缩 | 30-50% |
| P2 | 成本监控 | 可见性 |
| P2 | 预算告警 | 防失控 |
| P3 | 多 Key | 按项目追踪 |
7.3 预算推荐
| 团队规模 | 日预算 | 月预算 |
|---|---|---|
| 个人 | $5-10 | $100-200 |
| 小团队 (5人) | $20-50 | $400-1000 |
| 中团队 (20人) | $50-100 | $1000-2000 |
| 大团队 (50+) | $200+ | $4000+ |
结语
成本控制是长期使用 Claude Code 不可忽视的方面。通过实时成本监控、Prompt Caching、模型分级、上下文压缩、文件读取优化、预算告警和多 Key 管理,可以将 API 成本降低 50-80%,同时保持开发效率。
核心要点回顾:
- Prompt Caching:缓存系统提示,减少 50-70% 的 input 成本
- 模型分级:简单任务用 Haiku($0.25/M),日常用 Sonnet($3/M),复杂用 Opus($15/M)
- 上下文压缩:长会话定期压缩历史,避免 Token 二次增长
- 文件优化:跳过 node_modules、lock 文件、大文件,用 search 定位再读取
- 实时监控:用 Hook 记录每次 API 调用的成本和 Token 消耗
- 预算告警:设置日/月预算阈值,超限自动告警
- 多 Key 管理:按项目/团队分配不同 API Key,实现成本追踪
- 定期审查:周度/月度生成成本报告,识别优化空间
更多推荐



所有评论(0)