把 9 个 AI 模型串成一个统一网关: ai-showcase 架构实战
# 把 9 个 AI 模型串成一个统一网关: ai-showcase 架构实战
> 在 D:\\llm 38 个项目的基础上, 抽出一个统一网关层, 串 9 个 AI 模型 / 5 业务场景, 端到端跑通 A-B 测试和实时指标。文章详解 call_with_fallback / MockBackend / Prometheus 4 类指标等核心设计。
# 把 9 个 AI 模型串成一个统一网关: ai-showcase 架构实战
## 背景
我积累了 9 个独立部署的 AI 模型服务 (文本/代码/视觉/ASR/TTS/RAG/Math/视频/通用 LLM), 每个端口、协议、错误格式都不一样。
业务方想做一个客服 demo, 需要同时调 RAG + LLM, 每个 model 都要单独写客户端, **重复代码 800+ 行**。
痛点:
- 接 1 个新场景 = 2 天
- 错误处理五花八门
- 演示时 9 个服务任意一个挂了, demo 就翻车
- 没有统一指标, 不知道哪个场景慢 / 哪个模型贵
## 设计目标
> **把 9 个 AI 模型串成一个统一网关: ai-showcase 架构实战**
> 原文: [https://yunmzc.com/ai-blog/blog/ai-showcase-architecture/](https://yunmzc.com/ai-blog/blog/ai-showcase-architecture/)
> 更多 AI 全栈实战项目(38 个开源项目 + 完整技术博客),欢迎访问 [AI Portfolio](https://yunmzc.com/ai-blog)
>
> 点击链接加入群聊【人工智能AI大模型智能体应用交流群】:https://qm.qq.com/q/vAhgiEldoA 群号: 306671879
做一个 **统一网关层 (ai-showcase)**:
- 业务方只对一个端点发请求
- 网关负责路由 / 容错 / A-B 测试 / 指标埋点
- 业务方不需要知道底层是 9 个还是 90 个模型
## 核心设计
### 1. 场景化 API
不暴露 `POST /v1/chat/completions`, 而是 `POST /api/scenarios/{客服|代码|图片|语音|视频}/...`
业务方用业务术语调用, 不关心是哪个模型。
```python
# 业务方调用 (1 行, 跟 SaaS 一样)
r = await client.post("/api/demo/run", json={"scenario": "code_assistant"})
```
### 2. call_with_fallback 通用调用器
所有场景都走同一个 helper。流程 = **探测 → real → 失败 mock → 记录指标**。
```python
async def call_with_fallback(url, json_body, service_name, mock_fn, mock_args):
t0 = time.time()
if MOCK_MODE:
try:
# 健康探测
async with httpx.AsyncClient(timeout=2) as c:
base = url.split("/v1")[0].split("/api")[0]
hr = await c.get(f"{base}/health", timeout=1.5)
if hr.status_code != 200: raise RuntimeError(...)
# 真实调用
async with httpx.AsyncClient(timeout=30) as c:
r = await c.post(url, json=json_body)
r.raise_for_status()
return {"data": r.json(), "mode": "real", "latency_ms": int((time.time()-t0)*1000)}
except Exception:
MOCK_FALLBACK_COUNT.labels(service=service_name).inc()
txt, tokens = mock_fn(**mock_args)
return {"data": {"text": txt, "tokens": tokens}, "mode": "mock", "latency_ms": ...}
```
**核心 18 行** 替代了 800 行重复代码。
### 3. Mock 兜底动态化
mock 不写死, 而是从 `expected_keywords` 动态构造回复。
```python
class MockBackend:
@staticmethod
def code(task, language, is_finetuned, kws):
# 微调版: 中文注释 + 完整结构 + 命中 expected 关键词
# 基座版: 简洁, 关键词少
...
```
这样 **A/B 测试离线也能跑** (基座 28.5% vs 微调 98.0% 提升 69.5pp), 不会因为底层服务挂了 demo 翻车。
### 4. Prometheus 4 类指标
```python
REQUEST_COUNT = Counter("showcase_requests_total", "Total requests", ["scenario", "status", "mode"])
REQUEST_LATENCY = Histogram("showcase_request_duration_seconds", "Request latency", ["scenario"])
TOKENS_TOTAL = Counter("showcase_tokens_total", "Total tokens", ["model", "scenario"])
COST_TOTAL = Counter("showcase_cost_usd_total", "Total cost in USD", ["model"])
MOCK_FALLBACK_COUNT = Counter("showcase_mock_fallback_total", "Times fell back to mock", ["service"])
```
后端 10 行定义, **0 侵入接入**所有场景。
### 5. 结构化 JSON 日志 + Trace ID
```python
@app.middleware("http")
async def trace_id_middleware(request, call_next):
tid = request.headers.get("X-Request-ID") or uuid.uuid4().hex[:12]
token = _trace_var.set(tid)
...
response.headers["X-Request-ID"] = tid
```
每个请求 12 位 trace_id, ELK/Loki 检索一键定位。
## 效果
| 指标 | 之前 | 之后 | 提升 |
|---|---|---|---|
| 接 1 个新场景 | 2 天 | 4 小时 | 12x |
| 演示门槛 | 要启 9 服务 | 双击 start.py | ∞ |
| A/B 提升 | 没法量化 | 基座 28.5% → 微调 98.0% (+69.5pp) | 可量化 |
| 5 场景评估 | 无法跑 | 190 条 / 100% 通过 | 可重现 |
| 总成本估算 | 无 | 40 次 $0.0006 | 可监控 |
## 写在最后
"1 套代码串 9 模型" 的关键不是写多少代码, 而是 **抽象出统一的调用模式 + Mock 兜底 + 可观测性**。
面试经常被问"如何把多个 AI 服务统一管理", 答这一套足够。
## 完整代码
- [ai-showcase 仓库](https://github.com/yuanduan/ai-showcase)
- [5 业务场景评估报告](https://github.com/yuanduan/ai-showcase/blob/main/data/eval_reports/REPORT_xxx.md)
- [K8s 部署清单](https://github.com/yuanduan/ai-showcase/blob/main/k8s/manifests.yaml)
---
> 原文链接: [把 9 个 AI 模型串成一个统一网关: ai-showcase 架构实战](https://yunmzc.com/ai-blog/blog/ai-showcase-architecture/)
> 更多 AI 全栈实战项目(38 个开源项目 + 完整技术博客),欢迎访问 [AI Portfolio](https://yunmzc.com/ai-blog)
>
> 点击链接加入群聊【人工智能AI大模型智能体应用交流群】:https://qm.qq.com/q/vAhgiEldoA 群号: 306671879
浙公网安备 33010602011771号