成本优化指南
用 Python 按项目跟踪 API 支出并获取预算警报
一个简短的 Python 脚本将你的用量日志变成按项目统计的成本报告,并在月度预算的 50%、75%、95% 和 100% 时发出警告。

每次 API 调用后,向文件追加一行 JSON,给它打上项目名称标签,再用一个 30 行的 Python 脚本把该文件变成按项目统计的成本报告,并在月度预算达到 50%、75%、95% 和 100% 时发出警告。这正是 Anthropic 刚为 Claude Enterprise 团队提供的视图,只不过是用你自己的日志构建的。
假设一个余额同时供一个 agent、一个博客草稿脚本和一个小型支持机器人使用。月底到了,余额比你期望的低,却没有任何信息告诉你三者中是谁造成的。这就是本文要填补的空白。
Anthropic 发布了什么,你可以复制什么#
Anthropic 的公告(2026 年 10 月 28 日)涵盖智能报告、应用内调查和共享组预算,仅在 Claude Enterprise 套餐中普遍可用(来源)。这里有两部分值得关注。报告会把团队的工作归入工作流,并显示每个工作流的支出。共享预算是一个共享的月度数字,账单管理员会在达到 50%、75%、95% 和 100% 时收到邮件。Anthropic 补充说,这些报告用于规划投资,而不是评判个人。
你不需要企业套餐也能实现同样的形态。工作流只是你选择的一个标签,四个阈值只是代码中的一个列表。按照 Anthropic 给出的理由,按项目分组,而不是按人分组。
快速检查
你的预付费余额在归零时已经会停止请求。为什么还要在 50% 和 75% 时添加警告?
显示答案
答案: 因为归零太晚了:你想在还有钱可查的时候发现失控循环
余额是硬性停止,不是警报。提前的阈值能把 100% 时的意外变成第九天的一个问题。
日志:每个请求一行#
每个聊天补全响应都带有一个 usage 对象,其中包含 prompt_tokens 和 completion_。这就是报告所需的全部内容,再加上你自己添加的项目标签。这个包装器发送请求,并向月度文件追加一行(文件名每月变化,因此新月份从零开始):
import json, os, time
from openai import OpenAI
client = OpenAI(base_url="https://api.cheaptokens.dev/v1",
api_key=os.environ["CHEAPTOKENS_API_KEY"])
def ask(project, model, messages):
r = client.chat.completions.create(model=model, messages=messages)
with open(time.strftime("usage-%Y-%m.jsonl"), "a") as f:
f.write(json.dumps({"project": project, "model": model,
"prompt_tokens": r.usage.prompt_tokens,
"completion_tokens": r.usage.completion_tokens}) + "\n")
return r
ask("support-bot", "zai-org/GLM-5.3-Flash",
[{"role": "user", "content": "Where is my order?"}])使用 CheapTokens 文档 中的完整模型 ID(deepseek-、zai-);价格表中的短名称不被 API 接受。密钥来自环境变量,绝不来自文件。

脚本:按项目统计成本和阈值警报#
将此保存为 report.py。价格是占位符:从价格表复制当前价格(这里两个模型共用一个示例价格,只是为了保持算术可读)。
import json, os, sys, time
from collections import defaultdict
LOG = time.strftime("usage-%Y-%m.jsonl")
BUDGET = float(os.environ.get("BUDGET_USD", "10")) # example, substitute your own
STATE = LOG + ".alert"
LEVELS = [50, 75, 95, 100]
PRICES = { # USD per 1M tokens (input, output): example, substitute current prices
"deepseek-ai/DeepSeek-V4-Flash-0731": (1.0, 2.0),
"zai-org/GLM-5.3-Flash": (1.0, 2.0),
}
spend = defaultdict(float)
with open(LOG) as f:
for line in f:
r = json.loads(line)
p_in, p_out = PRICES[r["model"]]
spend[r["project"]] += (r["prompt_tokens"] * p_in
+ r["completion_tokens"] * p_out) / 1_000_000
total = sum(spend.values())
if total == 0:
sys.exit("log is empty")
for name, usd in sorted(spend.items(), key=lambda kv: -kv[1]):
print(f"{name:<8} ${usd:6.2f} {usd / total:5.0%}")
print(f"{'total':<8} ${total:6.2f} of ${BUDGET:.2f} ({total / BUDGET:.0%})")
last = int(open(STATE).read()) if os.path.exists(STATE) else 0
crossed = [l for l in LEVELS if total >= BUDGET * l / 100 and l > last]
if crossed:
print(f"WARNING: budget passed {crossed[-1]}%")
open(STATE, "w").write(str(crossed[-1]))最后四行是人们容易忘记的部分。如果没有这个小状态文件,脚本会在本月剩余时间的每次运行中都喊“已超过 50%”。有了它,每个级别只触发一次。
运行它并读取结果#
取一个四行的玩具日志(真实日志每个请求一行,所以有数千行):
{"project": "agent", "model": "deepseek-ai/DeepSeek-V4-Flash-0731", "prompt_tokens": 2400000, "completion_tokens": 300000}
{"project": "agent", "model": "deepseek-ai/DeepSeek-V4-Flash-0731", "prompt_tokens": 1000000, "completion_tokens": 100000}
{"project": "blog", "model": "zai-org/GLM-5.3-Flash", "prompt_tokens": 800000, "completion_tokens": 400000}
{"project": "bot", "model": "zai-org/GLM-5.3-Flash", "prompt_tokens": 500000, "completion_tokens": 100000}使用示例价格和预算 10,python report.py 会打印:
agent $ 4.20 65%
blog $ 1.60 25%
bot $ 0.70 11%
total $ 6.50 of $10.00 (65%)
WARNING: budget passed 50%agent 占了账单的近三分之二,而警告在 50% 时就出现了,而不是在最后。再次运行它,警告会保持安静,直到总额达到 75%。

要让这每月都生效,你需要三个步骤:
分步操作
分三步接入
记录每次调用
将所有调用都通过 ask(project, model, messages) 路由,并选择一个简短、稳定的项目标签。月中重命名标签会把一个项目拆成两行。
设置价格和预算
将价格表中的当前价格放入 PRICES,并导出你的限额:export BUDGET_。
按计划运行
每小时一行 cron 就足够了:0 * * * * cd ~/。如果你想把警告推送给自己,将输出发送到邮件或聊天命令。
在便宜模型上,一个月是什么样子#
数字只有变成你自己的数字才有用。假设玩具日志中的 agent 平均每步 30,000 输入和 2,000 输出 token,每个任务 20 步,每天两个任务。在 DeepSeek-V4-Flash 上一步花费 $0.00065;在 GLM-5.3-Flash 上,$0.00068。一天是 40 步,一个月是 1,200 步。移动滑块以匹配你自己的 agent:
成本计算器
agent 的一天:每任务步数乘以每天任务数
每次请求的输入 token 数: 30,000 · 每次请求的输出 token 数: 2,000 · 每日请求数: 40
| 模型 | 每天 | 每月 |
|---|---|---|
| GLM-5.3-Flash$0.02$0.04 | $0.0272 | $0.816 |
| DeepSeek-V4-Flash$0.015$0.10 | $0.026 | $0.78 |
| MiniMax-M2.7$0.05$0.15 | $0.072 | $2.16 |
以及 1,200 步的一个月,输入和输出一起计算:
| 模型 | 输入 $/1M | 输出 $/1M | 每次请求 | 1,200 次请求 | OpenRouter,1,200 次请求 |
|---|---|---|---|---|---|
| GLM-5.3-Flash | $0.02 | $0.04 | $0.00068 | $0.816 | $1.41 |
| DeepSeek-V4-Flash | $0.015 | $0.10 | $0.00065 | $0.78 | $2.86 |
| MiniMax-M2.7 | $0.05 | $0.15 | $0.0018 | $2.16 | $25.92 |
每次请求 30,000 个输入 token 和 2,000 个输出 token。每 100 万 token 的价格为当前价格,会随我们的资费一同变动;OpenRouter 列显示相同工作负载的参考价格。
总的来说,输入和输出一起计算,两个模型都比该表中 OpenRouter 的参考价格便宜,任何时段一个价格,没有高峰附加费。DeepSeek-V4-Flash 还允许每个答案最多 32,768 个输出 token 和 380K 上下文;GLM-5.3-Flash 有 390K 上下文。一旦报告显示哪个项目是重头,只切换该项目的 model 字符串就是一行改动,而且一个余额覆盖所有模型。关于单个请求实际使用多少 token,请参阅一个 API 请求使用多少 token;关于中途停止一次 agent 运行,请参阅跟踪 AI agent token 成本。价格和模型在 CheapTokens 模型页面上。
这里容易出错的地方#
PRICES中缺少某个模型。 脚本会停止,并给出一个指明该模型的KeyError。添加它;不要默认为零,否则该项目看起来是免费的。- 流式调用。 只有你要求时,最后一个块才会携带用量(OpenAI SDK 中的
stream_)。否则你的日志会有缺口。options={"include_ usage": True} - 失败的请求。 只记录具有
usage对象的响应;出错的请求没有可计数的内容。 - 按人报告。 将标签保持为项目或 agent 名称。关于人的报告会招致错误的结论。



