跳到主要内容

统一 API 接口

本节定位

一旦你的系统不只接一个模型,问题很快就会冒出来:

  • 不同 provider 的参数名不一样
  • 返回结构不一样
  • 错误处理不一样

这时真正有价值的就不是“再接一个模型”,而是:

先把模型调用入口统一起来。

学习目标

  • 理解为什么多模型系统需要统一 API 层
  • 理解统一 API 接口在工程上到底省了什么
  • 看懂一个最小 provider 抽象示例
  • 明白统一 API 不等于“所有模型都完全一样”

一、为什么统一 API 会变得重要?

1.1 当你只有一个模型时,还不明显

如果你的项目里只有一个模型,一个简单 client 往往就够了。

1.2 一旦开始多模型 / 多 provider

你就会面对这些问题:

  • A 模型叫 messages
  • B 模型叫 prompt
  • 有的返回 content
  • 有的返回 output_text
  • 有的 token 统计字段也不同

这时业务代码会迅速变得很乱。

所以统一 API 的核心价值可以先记成:

把 provider 差异收拢到一层,而不是让业务层到处知道这些差异。


二、统一 API 最常见的目标是什么?

通常至少包括:

  • 统一请求结构
  • 统一响应结构
  • 统一错误处理
  • 统一日志与 trace

2.1 一个最小统一请求结构

request = {
"provider": "demo_provider",
"model": "demo-chat-model",
"query": "退款政策是什么?"
}

print(request)

2.2 一个最小统一响应结构

response = {
"provider": "demo_provider",
"model": "demo-chat-model",
"answer": "课程购买后 7 天内且学习进度低于 20% 可退款。",
"usage": {
"prompt_tokens": 24,
"completion_tokens": 18
}
}

print(response)

这样做的好处是:

  • 上层业务逻辑只面对一套稳定结构

三、一个最小 provider 抽象示例

class ProviderA:
def chat(self, query, model):
return {
"text": f"A-provider reply: {query}",
"tokens": 30
}

class ProviderB:
def generate(self, prompt, model_name):
return {
"output_text": f"B-provider reply: {prompt}",
"usage": {"total_tokens": 28}
}

如果你直接让业务代码分别去调这两个 provider,代码会越来越碎。


四、统一适配层到底在做什么?

4.1 把不同 provider 翻译成同一种结构

class UnifiedClient:
def __init__(self):
self.providers = {
"provider_a": ProviderA(),
"provider_b": ProviderB()
}

def chat(self, provider, query, model):
if provider == "provider_a":
raw = self.providers[provider].chat(query=query, model=model)
return {
"provider": provider,
"model": model,
"answer": raw["text"],
"usage": {"total_tokens": raw["tokens"]}
}

if provider == "provider_b":
raw = self.providers[provider].generate(prompt=query, model_name=model)
return {
"provider": provider,
"model": model,
"answer": raw["output_text"],
"usage": raw["usage"]
}

return {"error": "unknown_provider"}

client = UnifiedClient()
print(client.chat("provider_a", "退款政策是什么?", "demo-1"))
print(client.chat("provider_b", "退款政策是什么?", "demo-2"))

4.2 这段代码真正重要的不是语法,而是分层

它在告诉你:

  • provider 差异应该尽量收拢在统一适配层
  • 上层业务代码最好只看到统一接口

这就是“统一 API”最实际的工程价值。


五、为什么统一 API 不等于“所有模型完全一样”?

这是一个特别容易误解的点。

统一 API 的目标不是假装所有模型没有区别,而是:

把共性抽出来,把差异控制在有限边界内。

例如不同模型仍然可能在这些方面有差异:

  • 上下文长度
  • 支持的工具调用能力
  • 多模态能力
  • 输出格式约束能力

所以统一 API 更像:

  • 统一入口
  • 不是统一能力

六、为什么路由(routing)会自然出现在这一层?

一旦有统一 API 层,接下来很自然就会问:

  • 哪类请求该去哪个模型?
  • 便宜模型够不够?
  • 高风险请求要不要走更强模型?

6.1 一个简单路由示例

def route_model(query):
if "总结" in query or "改写" in query:
return "provider_a", "cheap-model"
return "provider_b", "strong-model"

for q in ["帮我总结这段话", "退款政策是什么?"]:
print(q, "->", route_model(q))

统一 API 层很适合承担这种“模型路由入口”角色。


七、统一 API 层最常见的工程收益

7.1 更容易切模型

你不用每个业务模块都改一遍。

7.2 更容易打日志和做成本统计

因为所有请求都经过同一入口。

7.3 更容易做灰度和 fallback

例如:

  • 主模型失败后切备用模型
  • 特定请求走便宜模型

这都是统一入口特别容易发挥价值的地方。


八、最常见的误区

8.1 以为统一 API 就能抹平所有模型差异

不会。
差异依然在,只是你把它们组织得更可控了。

8.2 过早设计得太重

如果项目只有一个 provider,过度抽象反而可能是负担。

8.3 统一了输入输出,却没统一错误结构和日志

这样后面排障还是会痛苦。


九、小结

这一节最重要的不是写出一个 UnifiedClient,而是理解:

统一 API 层的核心价值,在于把多 provider 差异收敛到有限边界里,让上层业务面对稳定契约。

这一步做稳之后,多模型路由、fallback、成本优化这些工程能力才更容易做起来。


练习

  1. UnifiedClient 再加一个统一错误结构。
  2. 想一想:为什么说统一 API 是“统一入口”,而不是“统一能力”?
  3. 如果你的系统暂时只接一个模型,为什么不一定要过早做很重的统一抽象?
  4. 用自己的话解释:为什么统一 API 层很适合承接模型路由和 fallback?