共计 2608 个字符,预计需要花费 7 分钟才能阅读完成。
1. 为什么我们需要更好的调用方式?
刚开始用 AI 服务的 API 时,我经常遇到这些头疼问题:

- 明明测试环境跑得好好的,一上线就莫名失败
- 网络稍微波动就直接报错,连重试机会都没有
- 返回的 JSON 结构五花八门,解析时各种类型错误
- 根本不知道服务到底稳不稳定,全凭用户投诉才发现问题
后来和团队其他成员交流后发现,大家都踩过类似的坑。裸调 API 就像不带防护装备攀岩——也许能到山顶,但随时可能摔得很惨。
2. 两种武器选择:原始 HTTP vs 封装 SDK
2.1 原始 HTTP 调用的痛
// 典型的裸调用示例
const response = await fetch('https://api.ai-service.com/v1/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${API_KEY}`
},
body: JSON.stringify({prompt: 'Hello world'})
});
const data = await response.json(); // 危险!没有错误处理
这种写法至少有三大隐患:
- 网络问题直接导致程序崩溃
- 没有重试机制
- 响应数据结构完全不可控
2.2 推荐方案:类型安全封装
我们的改进目标是:
- 自动重试网络错误
- 严格的输入输出类型检查
- 生产环境所需的监控能力
3. 手把手实现健壮调用
3.1 基础封装(带重试)
// 核心请求函数
async function callWithRetry<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> {
let lastError: unknown;
for (let i = 0; i < maxRetries; i++) {
try {return await fn();
} catch (error) {
lastError = error;
// 指数退避:1s, 2s, 4s...
await new Promise(r => setTimeout(r, 1000 * 2 ** i));
}
}
throw lastError;
}
// 使用示例
const safeFetch = async (prompt: string) => {return callWithRetry(async () => {
const response = await fetch(API_ENDPOINT, {/* 配置同上 */});
if (!response.ok) {throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();});
};
3.2 用 Zod 做响应验证
import {z} from 'zod';
// 定义响应结构
const AIChatResponseSchema = z.object({id: z.string(),
choices: z.array(
z.object({message: z.string(),
index: z.number(),
finish_reason: z.string().optional()
})
),
usage: z.object({prompt_tokens: z.number(),
completion_tokens: z.number()})
});
// 安全的响应处理器
async function getAIResponse(prompt: string) {const raw = await safeFetch(prompt);
return AIChatResponseSchema.parse(raw); // 自动校验类型
}
4. 生产环境必备功能
4.1 超时控制
// 给 fetch 添加超时
function fetchWithTimeout(url: string, opts: RequestInit, timeout = 5000) {const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
return fetch(url, {
...opts,
signal: controller.signal
}).finally(() => clearTimeout(timeoutId));
}
4.2 监控埋点
// 简单监控示例
async function trackedFetch(prompt: string) {const start = Date.now();
let success = false;
try {const result = await getAIResponse(prompt);
success = true;
return result;
} finally {
// 上报到监控系统
reportMetrics({
operation: 'ai_chat',
duration: Date.now() - start,
success,
timestamp: new Date().toISOString()
});
}
}
5. 避坑经验分享
5.1 错误码处理
常见需要特殊处理的错误:
- 429 Too Many Requests:需要实现速率限制
- 401 Unauthorized:检查密钥轮换
- 503 Service Unavailable:触发熔断机制
5.2 安全注意事项
敏感信息过滤示例:
function sanitizeLog(data: any) {const clone = { ...data};
if (clone.headers?.Authorization) {clone.headers.Authorization = '***REDACTED***';}
return clone;
}
6. 思考与延伸
如果服务需要支持多地域部署,网关设计要考虑:
- 如何做智能路由(选择延迟最低的可用区)
- 跨地域的容灾切换策略
- 统一的监控指标聚合
一个可能的架构:
graph LR
A[客户端] --> B[全局负载均衡]
B --> C[区域代理 1]
B --> D[区域代理 2]
C --> E[AI 服务集群 A]
D --> F[AI 服务集群 B]
7. 我的实践心得
这套方案在我们团队落地后,AI 服务的可用性从 92% 提升到了 99.8%。最关键的是当出现问题时,我们能通过监控指标快速定位到是网络问题、服务过载还是业务逻辑错误。
建议从简单封装开始逐步迭代,先确保基本可用性,再完善高级功能。记住:没有完美的方案,只有最适合当前业务阶段的解决方案。
正文完
发表至: 技术分享
近两天内
