通过数据合约和 CI 检查确保 AI SDK 定价信息不过时,在 provider 调价时自动阻断引用旧价格的代码,适合接入多模型的公司。
AI agent SDK 经常把定价信息藏在错误的地方。
模型字符串在代码里,当前的价目卡在仪表盘上,公开文档引用的是提供商页面的旧版本,财务团队用的是上周的导出数据,支持团队在工单里看到的又是另一个数字。这些视图都不一定是不诚实的,但放在一起,就构成了一个脆弱的发布流程。
当上游提供商修改了 token 行条目、添加了上下文层级、改变了缓存规则,或者发布了新的并发限制时,生产环境的 SDK 不应该依赖口耳相传的知识。它需要一个定价事实之门(pricing source-of-truth gate):一份小小的数据契约和一个 CI 检查,在过时的费率生效之前将其拦截。
本文展示了一个针对 OpenAI 兼容网关的实现示例,将 DeepSeek、Kimi、GLM、Qwen 等国产模型家族通过 AIWave 这样的统一 API 路由出去。目标是实用的:让定价事实是可追溯的、可审查的、可测试的,并且在流量迁移前对 agent 代码可见。
本次采价于 2026 年 8 月 20 日检查了公开定价和运营页面。这些是来源事实,而非永久产品真理。
一个好的门不应该把这张表扁平化为一个 price_per_token 数字。每个提供商都有自己的计费词汇体系。DeepSeek 有缓存命中和缓存未命中的输入,还涉及一个并发层面。Kimi 将长上下文缓存作为核心。GLM 将缓存输入与输出分开计算。Qwen 添加了请求层级、思考 token 和工具调用。这个门应该保留这些结构。
这个门的存在是为了阻止四类发布失败。
第一,过时的文档。开发者阅读了快速入门,复制了一条 SDK 路由,然后根据一个在提供商更新之前曾经正确的数字来做预算。即使代码仍然能跑,这也是一种信任失败。
第二,静默的路由漂移。SDK 指向 deepseek-v4-pro,但策略文件是为另一个路由家族更新的。应用程序仍然在返回答案,但财务无法根据公开文档复现账单。
第三,缓存计算不匹配。文档只显示输入和输出,而提供商对缓存命中的输入单独计费。长上下文 agent 可能看起来比预期贵得多,也可能便宜得多——取决于重复的上下文是否命中缓存。
第四,糟糕的降级行为。429 重试或降级路由会成倍增加成本。如果 SDK 没有将价目卡日期和重试策略带入日志,事故复盘就会变成猜谜游戏。
这些失败很常见,因为定价被当作文案来处理。应该把它当作代码来处理。
从一个签入到代码库的 JSON 文档开始。它应该足够小以便 SDK 测试,也足够丰富以便财务审查。
{
"generated_at": "2026-08-20T13:00:00Z",
"cards": [
{
"public_model": "deepseek-v4-flash",
"provider_family": "DeepSeek",
"source_url": "https://aiwave.live/pricing",
"source_checked_at": "2026-08-20",
"unit": "1M tokens",
"currency": "USD",
"input": 0.638,
"cached_input": 0.0203,
"output": 1.914,
"context_tokens": 1000000,
"max_output_tokens": 384000,
"policy_note": "AIWave all-day public row"
}
]
}
这里有两个重要的细节。
第一,source_checked_at 字段是强制的。没有日期的费率不是生产事实。它只是一个带有小数点的谣言。
第二,policy_note 字段说明了这一行是直接提供商定价、统一网关行、官方计划行、批量行,还是账户特定行。例如,AIWave 的 DeepSeek 行不应该被描述为 DeepSeek 的官方峰值计划。它们是 AIWave 全天行。保持这些层级分离可以防止日后出现错误的比较。
CI 门应该拦截三类错误:缺失证据、不安全值和过期快照。
from __future__ import annotations
import json
from datetime import date
from pathlib import Path
from urllib.parse import urlparse
MAX_AGE_DAYS = 7
REQUIRED_FIELDS = {
"public_model",
"provider_family",
"source_url",
"source_checked_at",
"unit",
"currency",
"input",
"output",
}
def load_cards(path: str) -> list[dict]:
data = json.loads(Path(path).read_text(encoding="utf-8"))
return data["cards"]
def validate_card(card: dict, today: date) -> list[str]:
errors: list[str] = []
missing = REQUIRED_FIELDS - set(card)
if missing:
errors.append(f"missing fields: {sorted(missing)}")
if urlparse(card.get("source_url", "")).scheme != "https":
errors.append("source_url must be https")
checked_at = date.fromisoformat(card["source_checked_at"])
if (today - checked_at).days > MAX_AGE_DAYS:
errors.append("source snapshot is older than policy allows")
for field in ("input", "cached_input", "output"):
value = card.get(field)
if value is not None and value < 0:
errors.append(f"{field} cannot be negative")
if card.get("currency") != "USD":
errors.append("this SDK release expects USD rows")
return errors
if __name__ == "__main__":
failures = []
for card in load_cards("rate_cards.json"):
for error in validate_card(card, date(2026, 8, 20)):
failures.append(f"{card.get('public_model', '<unknown>')}: {error}")
if failures:
raise SystemExit("\n".join(failures))
这是有意为之的朴素。它不会在每次 CI 运行期间去抓取提供商页面。它只验证签入的契约。独立的刷新任务可以更新快照,但发布 CI 应该回答一个更窄的问题:即将发布的 SDK 是否携带了完整、有日期、内部一致的事实?
SDK 还应该在每个路由决策中携带定价元数据。这不意味着 SDK 计算最终发票。它意味着 SDK 留下足够的证据,让网关账本能够复现为什么选择了某条路由。
import os
from dataclasses import dataclass, asdict
from openai import OpenAI
client = OpenAI(
api_key=os.environ.get("AIWAVE_API_KEY", "YOUR_API_KEY_HERE"),
base_url="https://aiwave.live/v1",
)
@dataclass(frozen=True)
class RouteDecision:
model: str
task_kind: str
source_checked_at: str
policy_version: str
max_tokens: int
fallback_allowed: bool
def choose_route(task_kind: str) -> RouteDecision:
if task_kind == "long_context_code_review":
return RouteDecision(
model="kimi-k3",
task_kind=task_kind,
source_checked_at="2026-08-20",
policy_version="sdk-rate-gate-2026-08-20",
max_tokens=2600,
fallback_allowed=False,
)
return RouteDecision(
model="deepseek-v4-flash",
task_kind=task_kind,
source_checked_at="2026-08-20",
policy_version="sdk-rate-gate-2026-08-20",
max_tokens=1200,
fallback_allowed=True,
)
route = choose_route("support_summary")
response = client.chat.completions.create(
model=route.model,
messages=[{"role": "user", "content": "Summarize the incident timeline for engineering review."}],
max_tokens=route.max_tokens,
)
print({"response_id": response.id, **asdict(route)})
打印出来的路由元数据本身不是计费记录。它是连接键。网关可以附加实际的 token 计数、缓存命中 token 数、重试次数、状态、延迟和最终模型。当客户问为什么 SDK 使用了某条路由时,支持团队可以用工程团队在发布时审查过的同一策略版本作答。
一个强健的事实之门允许提供商特定字段存在,而不需要将每个模型强制塞进同一个模子。
对于 DeepSeek,需要包含缓存命中输入、缓存未命中输入、输出、上下文、最大输出、并发,以及 429 策略。因为 DeepSeek 文档说明了 user_id 隔离,还需要决定 SDK 是否通过 extra_body 传递一个隐私安全的租户标识符。
对于 Kimi K3,需要包含长上下文大小、缓存命中输入、缓存未命中输入、输出,以及该路由是否获准用于整个仓库或大文档工作负载。没有缓存可见性的长上下文路由很难做预算。
对于 GLM,需要包含缓存输入和输出行、工具能力、推理层级,以及任何可能影响发票行为的 agent/工具行。不要对可能调用视觉、网络搜索、图片、视频或 agent 工具的路由使用纯文本假设。
对于 Qwen,需要包含请求长度层级、Batch API 策略、思考 token 计费,以及工具费用。启用了思考的 Qwen 请求的计费方式可能与紧凑文本响应不同,即使公开模型名称相同。
契约可以通过添加 billing_dimensions 对象来支持这些:
{
"public_model": "qwen-route-example",
"provider_family": "Qwen",
"source_checked_at": "2026-08-20",
"billing_dimensions": {
"request_tiers": true,
"context_caching": true,
"thinking_tokens_bill_as_output": true,
"batch_api_has_separate_policy": true,
"built_in_tool_fees": true
}
}
SDK 不需要知道每个提供商的细节才能发起调用。它确实需要知道哪些细节存在,因为这些细节驱动警告、使用账本和发布审查。
发布工作流应该简单。
第一步:刷新公开来源快照。记录提供商 URL、使用 AIWave 行时的 AIWave URL、检索日期、货币、单位,以及关于计划或账户范围的任何备注。
第二步:像代码一样审查 diff。价格变化、上下文变化、输出上限变化或并发变化应该与 API 签名变化接受相同的审查纪律。
第三步:运行 CI 验证。如果缺少必填字段、日期过时、值为负数、模型名称不在 SDK 白名单中,或者文章/文档层引用的行与 JSON 契约不同,发布应该失败。
第四步:同时发布文档和 SDK。文档应该从同一份签入的 JSON 渲染,而不是从手工编辑的表格。
第五步:记录运行时路由决策。每个生产请求都应该携带模型、策略版本、来源日期、token 计数、缓存字段、重试次数、降级原因和状态。存储使用元数据,而不是提示词正文或真实客户标识符。
面向客户的文档不需要每一个内部控制项。他们只需要足够的信息来信任路由。
展示模型家族、公开模型 ID、支持的请求形状、上下文窗口、输出上限、定价来源日期、缓存类别和已知限制。解释该行是直接提供商行还是 AIWave 统一行。链接到来源页面。展示一个使用输入、缓存输入和输出分开计算的小计算器或公式。
避免绝对化的声明。一条路由可以可预测,而不一定要低于每个官方提供商行。一个模型可以在一类任务上表现强,在另一类上表现差。缓存行只有在工作负载实际重用上下文时才能降低重复上下文成本。精确比宽泛的营销语言更可信。
中国 AI 模型 API 发展很快。这对开发者来说是好事,但也让过时的定价变得危险。生产环境的 SDK 不应该携带只存在于博客文章、截图或私人电子表格中的数字。
定价事实之门将价目卡变成一个发布产物。它强制每条模型路由携带来源 URL、检索日期、计费维度和策略版本。它给 CI 一个具体的东西来拒绝。它给运行时日志一个连接键。它给支持团队和财务团队与工程团队在发布时使用的相同证据。
在你的下一个 agent SDK 发布之前,把定价契约变成构建的一部分。