模型价格动态获取缓存方法详解(评测平台计费表维护)

AI 大模型评测平台按月或按调用计量成本,单价来自各家供应商且会调整。把价格硬编码在配置里会迅速过期,每次调用都远程拉取又慢又脆弱。正确做法是把价格表集中存库,启动时加载进 Redis 并设置小时级 TTL,定时刷新与事件失效双管齐下,评测扣费时从缓存读取最新单价。下文给出缓存键设计、刷新策略与 Spring Boot 代码。

说明:本文基于通用工程实践对平台价格层做合理推演,非平台真实源码。方案适用于任意 Spring Boot + Redis 技术栈,读者应按供应商真实计费单位(每千 token / 每百万 token)对齐字段精度。

一、价格为什么不能写死

模型供应商按 token 计费,单价随版本迭代频繁变动。评测平台对接多家模型,每家的 input/output 单价、缓存命中价、最小计费单位都不同。若把价格写进 Java 常量,供应商调价当天计费就失真,月底对账与真实账单对不上。价格必须作为”会变化的数据”来管理,而非”不变的代码”。

把价格表放进数据库做单一事实源,应用层通过缓存读它,既保证可运营人员后台修改,又避免每次评测都查库拖慢链路。

二、缓存分层与键设计

2.1 单模型单价键

每个模型一条价格记录,键形如 price:{vendor}:{model},Hash 字段存 input_per_1k、output_per_1k、cache_hit_per_1k,单位为”每千 token 多少元”。

字段 含义 单位
inputper1k 输入单价 元 / 千 token
outputper1k 输出单价 元 / 千 token
cachehitper_1k 缓存命中单价 元 / 千 token

2.2 全量价格快照键

price:all 存整张价格表的 JSON 快照,供后台看板一次性读取,TTL 设 1 小时,到点自动失效触发重载。

三、动态获取与刷新策略

3.1 读时回源

扣费前先查 Redis,命中直接用;未命中从数据库加载并写回,避免缓存穿透把压力打到数据库。

  1. 读取 price:{vendor}:{model};
  2. 命中则返回,用于本次 token 成本估算;
  3. 未命中则从价格表数据库按 vendor+model 查询;
  4. 查到则写回 Redis 并设 TTL,返回单价;
  5. 查不到则抛配置缺失异常,阻断该模型评测扣费。
@Service
public class PriceService {
    private final StringRedisTemplate redis;
    private final PriceMapper priceMapper;

    public PriceService(StringRedisTemplate redis, PriceMapper priceMapper) {
        this.redis = redis;
        this.priceMapper = priceMapper;
    }

    public ModelPrice getPrice(String vendor, String model) {
        String key = "price:" + vendor + ":" + model;
        var cached = redis.opsForHash().entries(key);
        if (!cached.isEmpty()) {
            return toPrice(cached);
        }
        ModelPrice db = priceMapper.selectByVendorModel(vendor, model);
        if (db == null) throw new IllegalStateException("价格未配置: " + vendor + "/" + model);
        redis.opsForHash().putAll(key, fromPrice(db));
        redis.expire(key, Duration.ofHours(1)); // 小时级失效
        return db;
    }
}

3.2 定时刷新与事件失效

仅靠 TTL 会有”旧价残留一小时”的窗口。补两条刷新路径:定时任务每小时全量重载 price:all;运营后台改价时主动 DEL price:{vendor}:{model},下次读取即回源最新值,做到改完立刻生效。

@Scheduled(fixedDelay = 3600_000)
public void reloadAll() {
    List<ModelPrice> all = priceMapper.selectAll();
    redis.opsForValue().set("price:all",
        JsonUtils.toJson(all), Duration.ofHours(1));
}

/** 后台改价后调用,使缓存即时失效 */
public void invalidate(String vendor, String model) {
    redis.delete("price:" + vendor + ":" + model);
}

四、和成本计量的衔接

评测任务结束拿到 usage 的 input/output token 后,调 getPrice 取当前单价算出金额,再交给成本计量模块累加(见 Redis 实时成本监控一文)。价格缓存与成本计数共用同一个 Redis 实例,读取链路零额外网络跳数。

五、生产注意点

Redis 连接失败时不能让整次评测失败:价格读取包一层 try-catch,连不上就回退到数据库直查或本地兜底配置,并打告警日志。价格属敏感计费数据,审计日志应落持久库(MySQL / 审计表)而非仅存 Redis,避免缓存清空丢失变更历史。缓存键用 vendor+model 组合,防止不同供应商同名模型串价。货币单位统一以”元”为基准、精度保留四位小数,避免浮点误差在海量调用后累积放大,造成对账偏差。

五、价格来源与供应商对接

价格单一事实源建议放在平台自有数据库,由运营或定时爬虫从供应商官网同步。直接在生产链路调供应商定价接口风险高:外部依赖会让每次评测扣费都多一次网络往返,且供应商接口不稳定会拖垮计费。正确做法是”离线同步入库、线上只读缓存”,供应商接口只在校验环节偶尔比对,不进入热路径。同步任务记录每次拉取的成功时间与来源,便于排查”价格为何没更新”这类运营疑问,也让计费口径有迹可循。

六、缓存一致性边界

价格这类”改了要尽快看到,但短暂旧值可接受”的数据,用”TTL + 事件失效”已足够,不必上写穿(write-through)增加写延迟。真正的边界在计费精度:扣费用的是读取瞬间的单价快照,若供应商在评测中途调价,本批次沿用旧价是合理的——它对应已发生调用的计费合约,强行中途换价反而对账混乱。平台应在价格表加 effective_time 字段,新价生效后新批次自动采用,旧批次不受影响。

失效方式 触发 时效 适用
TTL 过期 到点自动 小时级 日常兜底
事件失效 后台改价 DEL 秒级 运营改价
定时重载 调度任务 小时级 全量校正

七、与成本展示的联动

后台看板读 price:all 快照渲染”各模型单价对比表”,运营改价后看板下次刷新即更新。评测报告里每条子任务的成本,由”实际 token × 当时单价”算出,可在报告页回放任意历史批次的花费。把价格快照版本号随评测任务一起落库,即便后续价格大改,历史报告仍能精确还原当时的计费口径,审计无忧。

常见问题(FAQ)

Q1:价格改了多久能生效?

后台改价主动删缓存键,下次读取即回源最新单价,秒级生效。

Q2:Redis 挂了会影响评测吗?

价格读取加兜底,连不上就回退数据库直查,评测不中断。

Q3:为什么还要定时刷新?

TTL 防止脏数据常驻,定时重载兜底修正未被事件失效的陈旧键。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 qiqicto@qq.com 举报,一经查实,本站将立刻删除。
赞 (0)
其AI的头像其AI普通用户

相关推荐

返回顶部