Sub2API 的模型价格和上下文窗口不能每次都从远端拉。加了本地兜底,拉不到的时候服务还能跑。
sub2api/model-pricing-fallback
模型代理服务里有一类数据很不起眼:模型价格表。
它不像鉴权、转发、流式响应那样站在主路径上,也不像账号池和代理池那样每天都能看见状态变化。不过只要系统需要展示预估成本、识别上下文窗口、区分模型能力,它就会变成基础依赖。
这类依赖最怕被写成“运行时去远端拉一下”。
网络正常时,它当然能工作;网络抖动、容器 DNS 异常、GitHub 访问失败、某个地区被限制、上游文件临时不可用时,一个本来不该阻断主流程的数据文件,就会把整条服务拖进不确定状态。
所以我更喜欢给价格表设计一条明确的降级链路。
价格表更适合按配置处理
模型价格数据看起来像外部资料,但对代理服务来说,它更像配置。
它通常包含几类信息:
- 模型标识。
- 输入和输出计价。
- 上下文窗口大小。
- 是否支持某些能力。
这些字段不一定每小时都变,但它们会影响用户对请求成本和模型能力的判断。把它们当成临时查询,就会出现一个麻烦:服务能不能正常展示信息,取决于那一刻能不能连上远端文件。
这类文件加载失败,不应该影响接口转发和管理页打开。
更合理的是:远端可以提供新鲜数据,但本地必须有一份可用副本。远端失败时,系统继续运行,只是明确告诉维护者“现在用的是本地兜底版本”。
镜像分支比直接依赖上游更稳
直接从上游公开文件读取,链路最短,也最脆。
上游路径变化、访问受限、请求被限流,都会影响运行时。中间加一层镜像分支,反而更适合个人服务。
这层镜像可以由 GitHub Actions 定期更新,把上游价格数据复制到自己仓库的某个分支。运行时优先读镜像地址,减少每次直连上游带来的不确定性。
这样做有几个好处:
- 上游变化先进入可观察的更新任务。
- 自己可以控制镜像地址和文件格式。
- 运行时读取路径更稳定。
- 出问题时能区分“上游坏了”和“我自己的镜像没更新”。
多复制这一份数据,主要是把外部不确定性从运行时挪到更新流程里。
本地副本是最后的兜底
镜像也可能失败。
所以项目里还应该保留一份本地副本。运行时流程可以很朴素:
先读远端镜像失败则读本地副本使用本地副本时写 warning这条路不是为了保证每次都拿到最新价格,而是为了让价格表下载失败时服务还能继续跑。
我后来按两个问题看它:数据够不够新,服务还能不能用。
远端成功,得到较新的价格表;远端失败,得到较旧但格式稳定的本地表。用户请求仍然能走,管理界面仍然能打开,日志里能看见当前处于降级状态。
对小服务器来说,这种降级比一次次重试远端更实际。
降级必须可见
本地兜底不能悄悄发生。
如果服务从本地副本读取价格表,却没有任何日志或状态提示,后面排查成本显示异常时就很麻烦。维护者会以为系统正在使用最新数据,实际它可能已经离线好几天。
所以降级时至少要记录:
- 远端读取失败的原因。
- 使用的是哪一份本地文件。
- 本地文件的大致版本或更新时间。
- 后续是否需要人工刷新。
这些信息不一定要展示给最后用户,但维护端应该能看到。
“可用但不新鲜”和“完全不可用”是两种状态。日志要把它们分开。
手动更新路径也要保留
自动化会失败。
定时任务可能没跑,镜像分支可能权限异常,本地服务器可能临时无法访问外网。此时文档里最好保留一条手动更新路径:从公开源下载新文件,覆盖本地副本,再由发布或部署流程带上去。
这条命令不用经常跑,但它让系统不依赖某个单点自动化。
很多个人服务都有自动任务,但手动接管的步骤没人写清楚,自动任务一坏就卡住。
格式稳定比字段多更重要
价格表里当然可以有很多字段。
但运行时最需要的是稳定解析。字段可以扩展,读取层不能因为一个新增字段或局部缺失就崩掉。更稳的做法是只依赖必要字段,并对缺失值给出明确默认或跳过策略。
比如:
- 识别不到的模型,别假装知道价格。
- 没有上下文窗口,就别在界面里展示一个猜测值。
- 远端字段变了,应该记录解析失败,别让整个服务启动失败。
价格表是辅助判断,不是主业务数据库。
辅助数据的失败范围应该被限制住。
这类缓存适合写进发布检查门
价格表本地副本还有一个好处:它能被发布检查门检查。
构建或发布前可以确认:
- 本地文件存在。
- JSON 格式可解析。
- 至少包含若干常用模型条目。
- 文件大小在合理范围内。
- 最后更新时间不会离谱。
这些检查不需要复杂,但能防止“空文件也被打包”“格式坏了才到线上发现”这种低级问题。
远端镜像负责更新,本地副本负责兜底。发布前检查的重点是:本地这份文件至少能被正常读出来。
我的处理方式:先读远端,失败就读本地
这件事给我的提醒很简单:外部数据不要都做成运行时硬依赖。
模型价格表、能力表、上下文窗口、公开规则列表,这类数据都可以有相似设计:
上游公开源 -> 自己的镜像 -> 本地副本 -> 可见降级上游负责提供新数据,镜像负责把外部变化变成可观察更新,本地副本负责让服务在网络不稳时继续工作,日志负责告诉维护者现在是不是降级。
对我这种小服务器来说,多放一份本地文件,比运行时反复赌外网稳定更省心。
服务不需要因为一个价格文件下载失败就变得脆弱。它只需要诚实地说:这次没拿到最新数据,我先用本地副本继续跑。
继续读