接入前检查
把项目里的 LLM 调用搜一遍。如果到处都是 openai.chat.completions.create,model 名写死在页面和队列里,先别接更多供应商。越接越乱。
| 层 | 负责什么 | 不该做什么 |
|---|---|---|
| UI | 收集用户输入 | 直接保存 provider key |
| Service | 定义任务类型 | 写死模型价格 |
| Router | 选择 provider 和 model | 处理业务文案 |
| Provider | 发请求和解析响应 | 判断用户套餐 |
| Logger | 记录 token、延迟、错误 | 默认存完整隐私内容 |
我一般会从三个变量开始抽:base_url、api_key、model。但只抽这三个还不够,生产里还要把 request id 和成本日志放进去。
怎么搭路由层
先定义内部模型别名,让业务侧只认识 fast-chat、code-heavy、cheap-summary 这种名字,不认识具体供应商。
type LlmTask = "fast-chat" | "code-heavy" | "cheap-summary";
const routes = {
"fast-chat": { provider: "openaiCompat", model: "gpt-fast" },
"code-heavy": { provider: "anthropic", model: "claude-code" },
"cheap-summary": { provider: "openaiCompat", model: "small-summary" }
};
Provider 层只管协议。OpenAI-compatible endpoint 通常围绕 /v1/chat/completions、Bearer key、model 字段和 streaming。Anthropic 原生接口有自己的消息结构和版本 header。不要在同一个函数里用一堆 if 硬拼。
所有请求都带 request_id。用户投诉「刚才生成失败」时,你能按 id 找到 provider、model、token、延迟、状态码,而不是去日志里凭时间猜。
加预算闸门。免费用户走小模型,付费用户走主模型;单用户每小时和每天都要有上限。没有预算闸门的 AI SaaS,账单会教你做人。
日志字段设计
| 字段 | 用途 |
|---|---|
| request_id | 串起前端、后端、provider 日志 |
| user_id / tenant_id | 算账和限流 |
| provider / model | 排查路由是否跑偏 |
| input_tokens / output_tokens | 成本和毛利 |
| latency_ms | 判断体验问题 |
| error_code | 触发重试和告警 |
完整 prompt 我只建议短期采样,而且要脱敏。独立开发者早期容易偷懒,等有企业客户后再补隐私策略会很痛。
跨地区使用和上线验证
开发期可以用多模型统一计费的 API 中转先把路由、日志和降级跑通。上线前再测三类真实任务:长输出 streaming、tool call、错误码返回。兼容接口最容易在边角能力上露馅。
常见失败原因
base_url多写或少写/v1- model 名是供应商别名,网关不认识
- streaming 响应格式和 SDK 不匹配
- 429 后没有按 retry-after 等待
- fallback 没有最大次数,失败时雪崩
- 日志里没有 token,毛利算不出来
还有一个坑是把「模型选择」交给用户。用户通常会想选高规格模型,但账单不会先到他那里。更好的做法是让用户选择效果档位,比如快速、标准、深度;后端再映射到具体模型和预算。这样你以后换供应商、改价格、临时降级,都不用改产品文案。
上线后第一周要盯两张表:每个任务的平均 token,以及每个模型的失败率。平均 token 决定毛利,失败率决定客服量。很多 AI SaaS 不是死在模型不好,而是死在「每个请求都用贵模型且没有上限」。路由层写得早一点,后面会少很多补丁。
还有产品层的兜底。模型路由失败时,按钮不要只变红。给用户一个任务状态、重试入口和简短解释;后台保留原始输入的哈希和任务 id。这样客服能补跑,用户也不会连点十次,把同一个任务送进队列十份。对一个人运营的产品来说,少一次重复生成,就是少一点账单和少一封解释邮件。别小看这些边角,付费用户多起来后,它们会变成真实成本项。