我给自己写了一个 mini OpenRouter:基于蓝耘 MaaS 的多模型路由网关实战

我给自己写了一个 mini OpenRouter:基于蓝耘 MaaS 的多模型路由网关实战

一、为什么需要"自己的网关"

前几篇我把蓝耘 MaaS 的 API 已经摸熟了------45 个模型、OpenAI 兼容协议、统一 Key 调用。但真要在生产环境用起来,会很快撞上几个工程问题:

  1. 该用哪个模型? 蓝耘有 25 个对话模型:qwen3.6-flash 便宜但能力一般,glm-5.3 强但贵。让每个业务开发自己挑,结果肯定是"全部用最贵的"。
  2. 模型挂了怎么办? 虽然蓝耘 SLA 不错,但任何单一模型都有限流、抖动、临时不可用的可能。直接调裸 API,一旦失败业务就 500。
  3. 成本到底花在哪? 月底账单"总消耗 100 万 token",但哪个业务线、哪个模型、哪个用户花的?算不清。

业界的标准答案是 AI Gateway------OpenRouter、Portkey、One-API 这类产品干的就是这事。但它们要么走 SaaS 订阅(多一层 vendor lock-in)、要么需要自己部署 K8s(小团队玩不起)。

我的方案 :蓝耘本身已经是"统一上游",我只需要在它前面再加一层薄薄的"智能路由 + 容错 + 计费"。用 Python 标准库写了 200 行 ,跑通全部功能。

二、架构设计

整个网关只有 4 个核心模块,全部跑在调用方进程内(无独立部署):

模块 职责 实现
Router 按规则选模型 一组 lambda 规则,从上到下匹配
Failover 失败自动切换 每个模型配一条备胎链
RateLimiter 控制并发 asyncio.Semaphore(5)
Metering Token 级成本核算 按模型定价表精确到分

对外只暴露一个方法 :gw.call(prompt),业务方完全不用关心下面是哪个模型在干活。

三、核心代码(精华版)

完整代码 200 行,这里只贴最关键的三段:

模型注册表 + 路由规则("配置即代码"):

python 复制代码
MODEL_REGISTRY = {
    "qwen3.6-flash":     {"price_in": 0.001, "price_out": 0.005, "tier": "cheap"},
    "deepseek-v4-flash": {"price_in": 0.001, "price_out": 0.002, "tier": "mid"},
    "glm-5.3":           {"price_in": 0.005, "price_out": 0.020, "tier": "premium"},
}

ROUTING_RULES = [
    (lambda p: len(p) > 100,                              "glm-5.3"),            # 长 prompt
    (lambda p: "代码" in p or "debug" in p.lower(),        "deepseek-v4-flash"), # 代码类
    (lambda p: len(p) < 50,                               "qwen3.6-flash"),     # 短问答
]

FALLBACK_CHAIN = {
    "qwen3.6-flash":     ["deepseek-v4-flash", "glm-5.3"],
    "deepseek-v4-flash": ["qwen3.6-flash",     "glm-5.3"],
    "glm-5.3":           ["deepseek-v4-flash", "qwen3.6-flash"],
}

带容错的主调用入口:

python 复制代码
async def call(self, prompt: str, request_id: str = "") -> CallRecord:
    primary = self.route(prompt)
    models_to_try = [primary] + FALLBACK_CHAIN.get(primary, [])

    for idx, model in enumerate(models_to_try):
        record = await self._try_call(model, prompt, request_id,
                                      fallback_from=models_to_try[idx-1] if idx > 0 else None)
        if record.ok:
            if idx > 0:
                self.stats.failovers += 1
            return record
    return record  # 全部失败

并发安全的 HTTP 调用(asyncio + urllib):

python 复制代码
async def _try_call(self, model, prompt, rid, fallback_from=None):
    async with self.sem:  # 信号量限流,最多 5 并发
        t0 = time.time()
        loop = asyncio.get_event_loop()
        d = await loop.run_in_executor(None, self._http_post, body)
        # urllib 是同步的,扔进 executor 不阻塞事件循环
        ...

注意几个工程细节:

  • run_in_executor 把同步的 urllib 包装成异步,避免引入 httpx/aiohttp 依赖
  • asyncio.Semaphore 控制并发上限,防止打爆上游
  • 每次调用记录 CallRecord(dataclass),便于后续审计/计费

四、实测场景

场景 1:路由策略------三种 prompt 各走各的模型

我设计了三种典型业务 prompt,让网关自动选择模型:

实测结果:

Prompt 字符数 路由到 耗时 成本
"你好" 2 qwen3.6-flash 9.59s ¥0.001600
"帮我 debug..." 20 deepseek-v4-flash 8.88s ¥0.000696
"撰写 5000 字报告..."×3 177 glm-5.3 12.95s ¥0.010571

最便宜的 qwen3.6-flash 一次只要 0.16 分钱,最贵的 glm-5.3 一次 1 分钱 ------价格差 6.6 倍。如果业务不区分场景全用 glm-5.3,一天 1 万次调用就是 ¥105,而合理路由后只要 ¥30 左右,省 70%。

场景 2:故障转移------主模型挂了自动切备胎

最考验网关能力的场景。我故意调一个不存在的模型 not-exist-model-xyz:

看输出:

css 复制代码
✗ 第 1 次尝试 [not-exist-model-xyz] HTTP 404
✓ 第 2 次尝试 [qwen3.6-flash] 成功  6.74s  ¥0.001771
  • 第一次 404 立刻返回,不到 100ms 就触发了备胎切换
  • 业务方收到的是成功响应,完全无感知
  • 网关记录了这次 failover(failovers: 1),后续可以告警

这种容错对生产环境至关重要------故障转移不是"模型挂了",更常见的是限流、超时、单条请求触发了上游 bug 。蓝耘返回的错误码很规范(HTTP 404、HTTP 402、HTTP 429 等),让容错逻辑可以写得非常干净。

场景 3:并发压测------10 个并发打满

光看单次延迟没意义,得看并发承载。发了 10 个并发请求,用信号量限制最多 5 个同时在飞:

实测数据:

指标 数值
成功率 10/10(100%)
总耗时 25.19s
平均延迟 12.10s
P50 12.32s
P95 14.87s
最大延迟 14.87s
QPS 0.40
总成本 ¥0.043793

几个观察:

  1. 零失败------蓝耘在 5 并发下完全稳定
  2. P95 - P50 只差 2.5 秒------延迟分布很平,没有长尾
  3. QPS 0.4 不算高------这是推理型大模型的常态,对比 GPT-4 同级
  4. 10 次调用成本不到 5 分钱------便宜到可以忽略

如果要更高 QPS,可以开大 Semaphore,或者在网关上做请求队列 + 批量提交。

场景 4:综合业务模拟 + 成本分析

最后模拟一天的真实流量,覆盖 8 种业务场景(客服、代码助手、内容创作、闲聊、Review、文档总结、快速问答、故障演示):

终端报表一目了然:

markdown 复制代码
总调用: 8   总成本: ¥0.069227   故障转移: 1 次

模型                      调用数      平均延迟       累计成本
--------------------------------------------------------------------------
qwen3.6-flash             5        10.92s    ¥0.055959
deepseek-v4-flash         2         8.64s    ¥0.001311
glm-5.3                   1        13.17s    ¥0.011957

成本占比可视化:

erlang 复制代码
qwen3.6-flash       80.8%  ██████████████████████████
glm-5.3             17.3%  █████
deepseek-v4-flash    1.9%

这就是 Metering 模块的价值------一眼看出 80% 的成本花在 qwen3.6-flash 上(因为它被调用最多),glm-5.3 虽然贵但只被路由了一次,所以总成本可控。

如果想再优化,下一步可以:

  • 给每个业务线打 tag,按业务线统计成本
  • 做预算熔断------某个业务当天超 ¥10 自动降级到便宜模型
  • 把 records 落库到 SQLite/PG,用 Grafana 画实时大盘

五、关键工程经验

这次实测下来,对蓝耘 MaaS 的平台能力有几个直接判断:

优点

  1. OpenAI 协议兼容度极高------我的网关代码可以无缝切到 OpenAI/DeepSeek 官方,只需要换 base_url + key
  2. 模型间切换无差异------qwen/deepseek/glm 三个模型用同一份代码调,message 结构、usage 字段完全一致
  3. 错误码规范------404(模型不存在)、402(余额不足)、429(限流),让容错逻辑能精确编程
  4. Token 计量精准 ------每个响应都带 usage.prompt_tokens / completion_tokens,分账零误差
  5. 45 个模型统一入口------意味着我的 FALLBACK_CHAIN 可以写得很激进,反正备胎有的是

踩过的坑

  1. 不同模型延迟差异大 :qwen3.6-flash 平均 10s,deepseek-v4-flash 8s,glm-5.3 13s------路由策略也得考虑延迟,不能只看价格
  2. max_tokens 不能给太小:推理型模型 reasoning 也占 quota,给 200 会返回空 content(上一篇踩过)
  3. stream + failover 组合复杂 :流式响应一旦开始输出就没法切备胎了,所以生产环境建议先 stream=false 失败,再 failover;stream=true 不重试
  4. Token 单位是"元/千 token" 不是 "元/百万":算成本时千万别搞错数量级

六、这套网关的价值

把这次实验放到生产语境下算笔账:

假设一个中型 AI 应用:

  • 日调用量:50,000 次
  • 如果全用最贵的 glm-5.3:50,000 × ¥0.012 = ¥600/天 = ¥18,000/月
  • 用 mini-gateway 智能路由:80% 走便宜的 qwen + 15% 走 deepseek + 5% 走 glm
    • 40,000 × ¥0.0016 + 7,500 × ¥0.0007 + 2,500 × ¥0.012 = ¥64 + ¥5.25 + ¥30 = ¥99/天 = ¥2,970/月
  • 每月省 ¥15,000,省 83%

而这套网关代码只有 200 行,跑在业务进程内,零额外基础设施成本。

总结

回到最初的问题:中小团队如何低成本用上多模型?

我的答案是分两步:

  1. 选一个统一 MaaS 上游------蓝耘这种"一个 Key 调 45 个模型"的平台刚好填补这个空白,省掉逐一对接 DeepSeek/通义/Kimi 的工程成本
  2. 在前面加一层薄网关------用 200 行 Python 解决路由、容错、计费这三个工程问题,比引入 OpenRouter/One-API 这种重型方案轻得多

大模型时代的工程竞争力,不在于你调了哪个模型,而在于你能不能用最低的边际成本,把模型的能力稳定地交付给业务。这次用蓝耘 MaaS + 200 行 Python 搭出来的 mini-gateway,是我目前看到的最小可行方案。

相关推荐
leobertlan3 小时前
痛苦系列 | DSP-01 从连续到离散:DSP基础与采样
android·后端
高频因子挖掘机3 小时前
同一只股票前复权和不复权价格对不上?先检查这几个口径
后端·github·api
Bazingga3 小时前
Harness学习笔记:从马具到工程外壳
后端
法欧特斯卡雷特4 小时前
Kotlin 新特性抢先看:伴生扩展与伴生块
后端·面试·开源
桃李醉春风4 小时前
被微信拒审那天,我才真正学会 Vibecoding:一个人 + AI,3 万行代码、1.8 万张素材的小程序全复盘
后端
她的男孩4 小时前
打印模板草稿能保存,一点发布就报主从关系:我们把校验拆成了两档
java·spring boot·后端
Bazingga4 小时前
Embabel学习笔记:把Agent的决策权从LLM手里拿回来
后端
用户8314550980314 小时前
基于 Firecracker 自建 Agent 沙箱集群:状态机 + 文件管理 + 进程编排,附 E2B 生产经验校准
后端
武子康5 小时前
LingBot-VA 2.0 深度解析:为什么要同时预测未来世界与机器人动作
人工智能·后端·agent