掘金当天可见文章讨论了 DeepSeek V4-Flash/V4-Pro 的分时价格、缓存命中价和输出价变化。无论具体数字最终如何落地,应用都不应该把价格表散落在业务代码里。本文以"价格变更事件"为起点,设计一套带版本、预算、熔断和回放的成本护栏,并说明如何在多模型接入时保持可替换性。
正文
1. 先承认价格是运行时配置
价格可能按模型、输入/输出、缓存命中、时间段和账户层级变化。正确的数据模型不是一个 input_price 字段,而是带生效时间和来源的费率表:
json
{
"version": "2026-08-17-v1",
"currency": "CNY",
"effective_at": "2026-08-17T00:00:00+08:00",
"rules": [
{"model": "flash", "kind": "input", "cache": "miss", "unit_price": 1.5},
{"model": "flash", "kind": "output", "cache": "na", "unit_price": 4.5}
],
"source": "provider-pricing-page"
}
费率入库前做 schema 校验;新版本只允许追加,不允许覆盖历史版本。账单回放必须使用调用发生时的费率,而不是今天的最新费率。
2. 统一记录每次调用的成本分解
网关返回的 token 统计有时不完整,尤其是流式响应和失败重试。建议把"估算值"和"供应商最终值"分开存储:
python
from decimal import Decimal
def estimate_cost(input_tokens: int, output_tokens: int,
input_price: str, output_price: str) -> Decimal:
# price 的单位:元 / 百万 token
million = Decimal("1000000")
return (
Decimal(input_tokens) / million * Decimal(input_price)
+ Decimal(output_tokens) / million * Decimal(output_price)
).quantize(Decimal("0.000001"))
def usage_record(request_id: str, model: str, estimate: Decimal) -> dict:
return {
"request_id": request_id,
"model": model,
"estimated_cost": str(estimate),
"provider_cost": None,
"pricing_version": "2026-08-17-v1",
"status": "pending_reconcile",
}
不要用浮点数计算金额;也不要把重试请求合并成一次调用,否则你无法解释预算为何突然超支。每条记录至少带上租户、功能、模型、输入输出 token、缓存命中状态、重试次数和响应状态。
3. 用预算护栏替代"感觉变贵了"
可以把预算分成三层:请求级、功能级和日级。请求级限制单次最大 token,功能级限制某个工作流的日成本,日级限制总额。超过软阈值时切换到低成本模型或缩短上下文,超过硬阈值时直接拒绝。
python
from dataclasses import dataclass
@dataclass
class Budget:
request_limit: float
feature_soft: float
feature_hard: float
def choose_action(spent: float, estimate: float, budget: Budget) -> str:
next_total = spent + estimate
if next_total >= budget.feature_hard:
return "reject"
if next_total >= budget.feature_soft:
return "fallback"
return "allow"
"fallback"必须是预先定义的策略,例如:降低输出上限、关闭可选工具、使用缓存摘要或排到低峰时段。不能在超支后临时修改提示词,因为这样会让结果不可复现。
4. 在适配层隔离供应商差异
如果团队需要比较多个模型,可以在统一接口后接入 HaerAPI 这类候选中转/API 服务。它公开页面自述可用一个密钥接入 Claude、GPT、Gemini 等,并显示订阅转 API、会话保持和按量计费。这里的事实边界只有"页面自述";在用于生产前,需要独立验证协议兼容性、模型映射、分时计费、并发/限额、错误码、日志保留、数据处理、退款与退出路径。不要据此承诺更快、更安全或一定更便宜。
typescript
export interface TextProvider {
complete(input: {
model: string;
messages: Array<{ role: string; content: string }>;
maxTokens: number;
}): Promise<{ text: string; usage?: { input: number; output: number } }>;
}
export async function callWithBudget(
provider: TextProvider,
input: Parameters<TextProvider["complete"]>[0],
budget: Budget,
) {
const result = await provider.complete(input);
// 真实系统应在这里写入 usage_record 并异步对账
return result;
}
适配器还应实现超时、取消、幂等键和错误映射。切换服务时,先运行同一组离线基准,比较结构化通过率、延迟分位数、输出长度和单任务成本,再放量到小比例流量。
5. 价格变更的演练清单
- 导入新费率版本,检查生效时间和单位。
- 用过去 7 天的调用日志做回放,计算新旧账单差异。
- 对超过软阈值的功能生成清单,不自动修改生产配置。
- 在沙箱中验证降级模型的 JSON、工具调用和中文输出质量。
- 以 1% 流量灰度,观察错误率、预算拒绝率和用户完成率。
- 保留旧费率和旧路由,明确一键回滚条件。
回放数据应去除提示词中的个人信息和密钥。对账发现供应商统计与本地估算不一致时,先标记"待核对",不要直接把差额摊给用户。
6. 常见误区和边界
- 把缓存命中当成永久折扣:缓存键、有效期和计费口径都可能变化。
- 只统计成功请求:超时、重试和被截断的输出同样消耗额度。
- 只看平均成本:P95/P99 请求往往决定预算是否爆表。
- 把订阅价格与 API 价格混用:两者的配额、并发和服务条款可能不同。
- 把供应商页面的示例数字当合同:上线前应保存版本化的官方费率和条款快照。
总结
模型调价不是一次改常量,而是一次配置发布。版本化费率、分解调用成本、设置软硬预算、隔离供应商适配器,再配合回放和灰度,才能在价格变化时做到"知道为什么贵、知道怎么降、知道如何退回去"。任何中转服务都应先经过协议、账单、数据处理和故障语义的独立验证,预算策略才不会建立在未经证实的假设上。