01-模型网关与Provider适配
核对日期:2026-05-13。
不稳定项:模型 API、streaming 协议、工具调用格式、usage 字段、错误码、Batch 能力、供应商数据保留政策变化较快;生产接入前必须重新核对官方文档。
1. 学习目标
本专题聚焦 LLMOps 的入口层:如何把模型调用从业务代码中的零散 API 请求,升级为可治理、可审计、可扩展的模型访问层。
学完后你应该能做到:
- 解释为什么业务系统不应该直连多个模型供应商。
- 设计内部 AI SDK、LLM Gateway 和 Provider Adapter 的边界。
- 定义统一请求、统一响应、统一错误和统一 usage 字段。
- 处理 streaming、工具调用、结构化输出和 provider request id 差异。
- 识别模型网关自身的单点风险、合规风险和抽象过度风险。
2. 核心机制
推荐调用链:
业务应用
-> 内部 AI SDK
-> LLM Gateway
-> Provider Adapter
-> 模型供应商
每一层的职责不同:
| 层 | 职责 | 不应该做 |
|---|---|---|
| 业务应用 | 传入任务、上下文和业务约束 | 直接拼供应商参数 |
| 内部 AI SDK | 标准化调用体验、携带 trace、处理基础错误 | 内置复杂路由和预算规则 |
| LLM Gateway | 鉴权、路由、限流、预算、观测、安全策略 | 理解每个业务细节 |
| Provider Adapter | 映射供应商参数、错误、流式事件和 usage | 把供应商私有能力暴露到业务层 |
关键判断:SDK 解决“开发者怎么调用”,Gateway 解决“组织怎么治理”,Adapter 解决“供应商怎么适配”。
3. 统一请求模型
一个生产可用的标准请求至少包含:
| 字段 | 说明 |
|---|---|
tenant_id | 租户或团队,用于预算和隔离 |
user_id_hash | 脱敏用户标识,用于审计和限流 |
feature | 业务功能,例如 support_draft |
task_type | 分类、摘要、RAG、Agent、代码等 |
risk_level | low / medium / high |
messages | 标准模型输入 |
tools | 可用工具 schema |
output_schema | 结构化输出约束 |
prompt_version | Prompt 版本 |
policy_version | 安全策略版本 |
trace_id | 调用链 ID |
示例:
{
"tenant_id": "team-rd",
"user_id_hash": "u_8f31",
"feature": "knowledge_qa",
"task_type": "rag_qa",
"risk_level": "medium",
"prompt_version": "kb-qa-v0.3",
"policy_version": "ai-policy-v1",
"trace_id": "trace_20260513_001",
"messages": [],
"tools": [],
"output_schema": "answer-v2"
}
4. 统一响应模型
响应不要只返回文本,至少要返回:
| 字段 | 说明 |
|---|---|
output | 文本或结构化输出 |
model | 实际使用模型 |
provider | 实际供应商 |
usage | token、缓存 token、费用估算 |
latency_ms | 总耗时 |
first_token_ms | 首 token 延迟 |
finish_reason | stop、length、tool_calls、error 等 |
fallback_used | 是否发生 fallback |
provider_request_id | 上游请求 ID |
trace_id | 内部 trace |
这样才能定位:
- 为什么这次比上次贵。
- 为什么同一问题今天换了模型。
- 哪个 provider 出错。
- 是否发生 fallback 或重试。
5. Provider Adapter 设计
Adapter 需要屏蔽但不能抹平所有差异。
应该统一:
- 消息格式。
- streaming event。
- usage 字段。
- 错误码分类。
- request id。
- 超时和重试策略。
- 工具调用结构。
- 结构化输出解析。
应该保留:
- provider 原始错误摘要。
- provider 特有能力标记。
- 模型能力元数据。
- 区域和数据策略差异。
反模式是把所有 provider 压成最低公分母,导致高级能力无法使用;另一个反模式是完全不抽象,业务代码里到处都是供应商分支。
6. Streaming 适配
不同供应商的 streaming 事件结构可能不同,内部应统一成事件流:
message_start
content_delta
tool_call_delta
usage_delta
message_end
error
生产注意点:
- streaming 中断时要区分用户取消、网络断开、provider 失败。
- 已输出给用户的内容不一定能自动回滚。
- 如果中途发生工具调用,必须保留 tool call id 和参数摘要。
- 最终 usage 可能只在结束事件中返回,日志要补齐。
7. 错误分类
统一错误类型比保留原始错误码更重要。
| 类型 | 示例 | 处理 |
|---|---|---|
rate_limited | 429、配额超限 | 退避、排队、切备用 |
timeout | 上游超时 | 有条件重试或降级 |
invalid_request | 参数不合法 | 不重试,修正代码 |
auth_failed | key 无效 | 告警,停用 provider |
safety_blocked | 安全策略拦截 | 返回安全提示 |
schema_failed | 输出无法解析 | 有限重试或拒答 |
provider_down | 服务不可用 | 熔断和 fallback |
错误分类要进入 trace 和监控面板,否则线上只会看到一堆混乱的 500。
8. 密钥和权限
模型密钥不应该出现在:
- 前端。
- 移动端。
- 小程序端。
- 普通业务配置文件。
- 日志。
- 评测样例。
推荐方式:
- Gateway 保存供应商密钥。
- 业务方使用内部虚拟 key。
- 虚拟 key 绑定租户、功能、预算、模型白名单。
- 高风险功能需要额外审批。
- key 轮换和吊销要有审计。
9. 工程案例
9.1 从直连模型迁移到 Gateway
现状:
- 客服系统、知识库系统、运营工具各自保存供应商 key。
- 每个系统都实现一套 retry。
- 成本只能看总账单。
改造:
- 所有调用走内部 AI SDK。
- SDK 自动注入 feature、tenant、trace 和 prompt_version。
- Gateway 做预算、路由、日志和 provider adapter。
收益:
- 统一密钥治理。
- 统一成本报表。
- 模型升级不需要每个业务改代码。
9.2 Adapter 抽象过薄
表现:
- 业务代码里出现
if provider === ...。 - 工具调用格式散落在页面或服务里。
- usage 字段不统一,成本统计缺失。
修正:
- Adapter 输出统一 response。
- 保留 provider metadata,但不让业务依赖原始结构。
- 为每个 provider 建 contract test。
10. 常见反模式
| 反模式 | 表现 | 风险 | 修正 |
|---|---|---|---|
| 业务直连供应商 | 每个服务自己调 API | 密钥和成本失控 | 统一 SDK/Gateway |
| Gateway 什么都做 | 写入业务规则和复杂 prompt | 变成巨型服务 | 保持平台职责 |
| 抽象最低公分母 | 高级能力都用不上 | 创新受阻 | 标准字段 + 能力声明 |
| 不记录 request id | 上游问题无法追踪 | 排障困难 | 保存 provider request id |
| streaming 不归一 | 前端适配多个协议 | 体验不稳定 | 统一事件流 |
| 错误码不分类 | 全部变 500 | 无法治理 | 建错误分类表 |
11. 练习
为一个 RAG 问答系统设计内部 AI SDK 请求/响应结构,要求包含:
- tenant、user、feature、task_type。
- prompt_version、model、provider。
- usage、latency、trace。
- citations 和 schema 校验结果。
- fallback 和 retry 标记。
再说明:哪些字段进入日志,哪些字段需要脱敏,哪些字段不能记录。
12. 验收题
- 内部 AI SDK、LLM Gateway 和 Provider Adapter 的职责边界是什么?
- 为什么业务系统不应该保存供应商 key?
- Provider Adapter 应该统一哪些差异,保留哪些差异?
- streaming 适配为什么比普通请求更容易出问题?
- 为什么
provider_request_id对生产排障很重要? - 抽象过薄和抽象过厚分别会造成什么问题?