开发者因OpenAI官方账单缺乏细节而自建监控系统,发现两个功能的成本差异达100倍。这个真实案例暴露了API成本的隐性问题,对使用AI API的程序员有直接警示价值。
确定 100 倍成本差异
更新(2026 年 5 月 4 日):一位读者(Gary Stupak,评论区)指出 Cloudflare AI Gateway 支持自定义元数据头(cf-aig-metadata),让你可以从应用中将租户/功能/会话 ID 传播到网关日志。
如果你已经在 Cloudflare 技术栈上,从那里开始——Gateway 成为你的信息源,自定义仪表板则是验证而非主要工具。
如果你不在 Cloudflare 上(或想理解应该记录什么),这篇文章的其余部分仍然适用——在公众面前出错正是经验教训能留下来的方式。
简述:OpenAI 的账单页面显示总支出。不显示是哪个功能、哪个租户、哪个对话造成的。对多租户 AI 产品来说,那就像在闭眼飞行。我构建了一个 3 文件的监控系统——一个包装层、一个表、一个仪表板——给我精确到 8 位小数的单次调用成本。我第一次打开时,就发现了两个我一直认为相似的功能之间的 100 倍成本差异。
现在打开 platform.openai.com/usage。你会看到:
你看不到的:
对于副业项目,还好。对于生产级 AI 产品,你就是在瞎蒙。
我在构建一个多租户 AI 销售聊天机器人——每家店铺是一个独立客户,每家店铺有多个功能:聊天完成、嵌入、图像分析、资料提取。单个客户消息可能触发 1–3 次 OpenAI 调用。
当我上线时,OpenAI 告诉我昨天花了 $4.27。
这个数字毫无用处。是一次昂贵的图像分析?某家店铺疯狂发送数千条消息?某个循环重复调用同一个 API?没办法知道。
所以我自己构建了可观测性。三个文件。一个下午。
核心思路:不直接调用 OpenAI。调用一个包装层,它度量一切并异步记录。
OpenAI 的 API 返回 token 计数,但不返回成本。你从一个硬编码表自己计算:
// Last checked: 2026-04-15 — https://openai.com/pricing
const PRICING: Record<string, { input: number; output: number }> = {
"gpt-4o": { input: 2.50, output: 10.00 }, // per 1M tokens
"gpt-4o-mini": { input: 0.15, output: 0.60 }, // per 1M tokens
};
这里是核心比例:
gpt-4o 对于相同 token 数大约比 gpt-4o-mini 贵 16 倍。如果你用 gpt-4o 处理 gpt-4o-mini 能处理的任何任务,你在烧钱。仪表板让这个可以逐次调用地看到——这正是你决定哪个模型用在哪里时需要的。
import OpenAI from "openai";
import { createAdminClient } from "@/lib/supabase/admin";
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const PRICING: Record<string, { input: number; output: number }> = {
"gpt-4o": { input: 2.50, output: 10.00 },
"gpt-4o-mini": { input: 0.15, output: 0.60 },
};
interface LogMeta {
storeId: string;
conversationId?: string;
leadId?: string;
endpoint: string;
functionCalled?: string;
searchQuery?: string;
productsFound?: number;
}
export async function loggedChatCompletion(
params: OpenAI.Chat.Completions.ChatCompletionCreateParams,
meta: LogMeta
) {
const start = Date.now();
const result = await openai.chat.completions.create(params);
const duration = Date.now() - start;
const tokens = result.usage;
const rates = PRICING[params.model as string] || PRICING["gpt-4o-mini"];
const cost = tokens
? (tokens.prompt_tokens * rates.input +
tokens.completion_tokens * rates.output) / 1_000_000
: 0;
// Fire-and-forget log — never blocks the response
const supabase = createAdminClient();
supabase
.from("api_logs")
.insert({
store_id: meta.storeId,
conversation_id: meta.conversationId,
lead_id: meta.leadId,
endpoint: meta.endpoint,
model: params.model,
prompt_tokens: tokens?.prompt_tokens,
completion_tokens: tokens?.completion_tokens,
total_tokens: tokens?.total_tokens,
cost,
duration_ms: duration,
function_called: meta.functionCalled,
search_query: meta.searchQuery,
products_found: meta.productsFound,
status: "success",
})
.then(() => {})
.catch(() => {}); // Silent fail — monitoring never breaks the app
return { result, cost, duration };
}
关键模式:火速前发
这一行让它对生产环境来说是安全的:
.then(() => {}).catch(() => {}); // Silent fail
日志插入不被 await。如果数据库宕机、网络打嗝、表还不存在——用户的响应仍然能返回。
在我的测试中,日志插入耗时 15–40ms。聊天完成耗时 800–2500ms。如果我 await 日志,我就给每个请求增加 2–5% 的延迟,对用户没有任何好处。
监控绝对不能拖慢它正在监控的东西。这是这里唯一重要的规则。
我运行这个模式已有好几周,在数千条中丢失了大概 2–3 条日志。可以接受的权衡。
改动之前:
const response = await openai.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "user", content: customerMessage }],
});
改动之后:
const { result, cost, duration } = await loggedChatCompletion(
{
model: "gpt-4o-mini",
messages: [{ role: "user", content: customerMessage }],
},
{
storeId: store.id,
conversationId: conversation.id,
leadId: lead.id,
endpoint: "chat",
}
);
接口相同,多一个参数。全代码库查找替换:10 分钟。
CREATE TABLE api_logs (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
store_id UUID REFERENCES stores(id),
conversation_id UUID,
lead_id UUID,
endpoint TEXT NOT NULL,
model TEXT,
prompt_tokens INT,
completion_tokens INT,
total_tokens INT,
cost DECIMAL(10,8),
duration_ms INT,
function_called TEXT,
search_query TEXT,
products_found INT,
status TEXT DEFAULT 'success',
error TEXT,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX idx_api_logs_store ON api_logs(store_id);
CREATE INDEX idx_api_logs_created ON api_logs(created_at);
CREATE INDEX idx_api_logs_endpoint ON api_logs(endpoint);
列就是你能切片的维度。每一个都回答了 OpenAI 仪表板无法回答的问题:
store_id → "哪个租户花钱最多?" 在多租户 SaaS 中,一家店铺的成本可能是另一家的 10 倍。没有这一列你永远看不到。
endpoint → "昂贵的部分是聊天,还是图像分析?"
conversation_id + lead_id → "这次对话花了多少钱?这个客户花了多少?"
function_called + search_query + products_found → 调试列。当客户说"给我显示红色裙子",机器人什么都没返回时,你可以检查:它调用了搜索函数吗?用什么查询?返回了多少产品?这给了我数小时的调试时间。
duration_ms → 延迟。仪表板中色码显示:绿色 <1.5s,黄色 1.5–3s,红色 >3s。
error → 失败的调用仍然消耗 prompt tokens。OpenAI 对它们收费。追踪它们。
一个细节容易被忽视:cost DECIMAL(10,8)。8 位小数。
单个 gpt-4o-mini 聊天完成大约花费 $0.00013。用 DECIMAL(10,2),每个调用都舍入到 $0.00,你的合计数据就一文不值。分数便士在规模上很重要。
API 路由(/api/admin/logs/route.ts)采用过滤条件(startDate、endDate、endpoint、storeId)并返回聚合数据:
{
summary: {
totalRequests, totalTokens, avgTokensPerRequest, avgLatency, totalCost
},
dailyTokens: [{ date, prompt, completion, total }, ...],
hourlyActivity: [{ hour, count }, ...],
endpointBreakdown: [{ endpoint, count, cost, percentage }, ...],
modelBreakdown: [{ model, count, cost, percentage }, ...],
storeBreakdown: [{ storeId, storeName, count, cost }, ...],
}
UI 故意保持无聊:
API 做了繁重工作。UI 只是渲染预聚合数据。没有客户端计算,没有惊喜。
某个生产日期的真实数据:
按功能分解的成本:
打开这个视图的瞬间,两件事跳了出来:
其一。用 gpt-4o 做图像分析的成本大约比用 gpt-4o-mini 聊天每次调用贵 100 倍。尽管只有大约 1% 的调用是图像分析,它们却吃掉了大约 10% 的预算。这改变了我对哪些功能值得用 gpt-4o vs 哪些可以用 gpt-4o-mini 的看法。
其二。聊天 endpoint 的平均 prompt tokens 每次调用远高于我的估计。仪表板显示了症状;调查发现我在每个单次响应中都把整个对话历史作为上下文发送。那是我写过文章的一个单独架构修复——这篇文章的重点是,如果仪表板没有向我展示症状,我永远不会去寻找这个 bug。
这就是循环。你无法优化无法测量的东西。你无法测量无法检测的东西。而通用账单仪表板不会检测你的应用。
1. OpenAI 的仪表板是账单工具,不是可观测性工具
它告诉财务部门应该收费什么。它不告诉工程部门应该修复什么。工作不同。
2. 火速前发不可妥协
如果你的监控阻塞了请求路径,你就让产品变差了。可观测性的整个意义在于它是不可见的,直到你查看它。永远非 await 插入。登录错误上永远静默失败。
3. 八位小数,不是两位
把成本存为 DECIMAL(10,2),每个调用都舍入为零。AI 成本是每次调用分数便士。把它们当分数便士对待。
4. 维度就是产品
总成本是个数字。单租户成本、单功能成本、单对话成本才是洞察。你记录的列决定了你能回答什么问题。当你构建功能时就添加列,不要等到有问题了再加。
5. 硬编码定价。手动更新它。
没有 OpenAI 定价 API 可以查询。硬编码费率并加上你最后检查的日期注释,当 OpenAI 改变时更新它。两行代码,一个月三分钟。
// Last checked: 2026-04-15 — https://openai.com/pricing
const PRICING = {
"gpt-4o": { input: 2.50, output: 10.00 },
"gpt-4o-mini": { input: 0.15, output: 0.60 },
};
一旦基础版本到位,这是升级路径:
延迟百分位数。平均延迟会撒谎。追踪 p50、p95、p99。平均可能是 1.2s,但如果 p99 是 8s,100 个用户中有 1 个在经历糟糕的体验。
单租户预算告警。每家店铺每天 $1 的阈值。超过时发送 Slack/邮件。捕捉失控循环、生成巨大输出的 prompt injection,或意外使用量激增的店铺。
endpoint 的错误率。总错误率隐藏分布。聊天 2% 错误和图像分析 15% 错误与两者都 8% 是不同的问题。
转化成本。如果你的 AI 存在是为了驱动业务成果(销售、注册、完成),把日志连到成果表。现在你有每次对话的 ROI,而不仅是每次对话的支出。
模型迁移追踪。当你把一个功能从 gpt-4o 切换到 gpt-4o-mini,成本下降应该是可见的。model 列让迁移前后比较变成琐碎的事。
三个文件。一个下午。大约 400 行代码。
一个拦截每个 API 调用的包装层。一个有足够维度来切片数据的表。一个把它聚合成你可以作用的东西的页面。
你不需要 LangSmith 或 Helicone 或 Datadog(如果你倾向于它们,它们很好)。你需要最小可能的工具来回答"是哪个功能、哪个租户、哪个对话"——因为那是你的账单仪表板无法回答的问题。
我第一次打开我的时,我捕捉到了两个我一直认为相似的功能之间的 100 倍成本差异。我捕捉到它因为我构建了能看到它的镜头。
在你上线前构建镜头。或者——更诚实地说——在你上线那天构建它,在你忘记前。
我在构建 Provia——一个为阿拉伯语电商店铺设计的 AI 销售聊天机器人。关注更多关于在加沙以紧张预算构建 AI 产品的文章。
一些评论可能仅对已登录访客可见。登录查看所有评论。
如需进一步行动,你可以考虑屏蔽此人和/或报告滥用。