1,847次调用里,1,385次根本没走OpenAI服务器。一个管工业设备的非程序员,用SQLite和几行Node.js,把API成本压到了原来的60%。
这事听起来像那种"离职程序员 revenge story",但主角John(化名,原文未提真名)的身份是CMMS(计算机化维护管理系统)主管——日常工作是盯着工厂里的空调和泵,不是写代码。他的团队需要AI生成工单、预测性维护警报、供应商沟通函、培训文档,但同一个"给HVAC写工单"的prompt,用户能问三遍,收三遍钱。
重复提问,重复付费。这不是OpenAI的bug,是标准商业模式。
John算了笔账:$0.002一次看着便宜,但工厂环境里,一线员工查同样的问题、用同样的措辞,是常态。规模上去之后,"便宜"变成了"很贵的便宜"。
解决方案:在应用和OpenAI之间插一层
架构很简单,但有效。用户的请求先打到他的代理服务,代理算prompt的hash,查本地SQLite缓存,命中直接返回,没命中再走OpenAI API,同时把结果写进缓存。
流程变成:你的App → AI优化代理 → [缓存检查] → 要么直接返回,要么调用OpenAI → 回写缓存。
核心代码就两段。getCacheKey查库,setCacheKey带TTL(默认24小时)写库。请求处理器里,命中就记analytics.recordHit(true),没命中就调OpenAI、写缓存、记recordHit(false)。
John没用什么Redis集群,SQLite单机扛住了。hash函数直接对req.body做,没做语义去重——"HVAC maintenance"和"HVAC 维护"算两个key,但他评估过,工厂环境里的prompt重复度够高,精确匹配就够用了。
语义缓存是更优雅的方案,但优雅需要成本。John的选择是:先解决75%的问题,用20%的复杂度。
一周跑出来的数据
• 总请求:1,847次
• 缓存命中:1,385次
• 缓存未命中:462次
• 命中率:75%
• 成本节省:约40%
他给了个更直观的换算:如果你月付$100给OpenAI,这套东西能帮你省$40-60。省下来的钱,够买好几个Cursor订阅了。
75%的命中率意味着,每4个请求里3个没花API钱。但John也坦诚,这个比例和他的使用场景强相关——工厂CMMS的查询高度模式化,换到创意写作场景,命中率会断崖下跌。
缓存不是银弹,是场景药。
踩过的坑:设备指纹和Stripe
John把这个代理做成了可授权的产品,需要识别设备但不强制登录。他用node-machine-id取机器ID,加上os.hostname()做sha256,生成设备指纹。
这个方案在Docker容器里会翻车——容器hostname随机,机器ID也可能重复。John的应对是:明确不支持容器部署,目标用户是中小工厂的本地部署场景。
Stripe集成部分原文没展开,但提到了一个细节:他需要在代理层处理license验证,再决定要不要服务请求。这相当于把商业逻辑也塞进了缓存层,架构上不够纯粹,但对他来说,"一个服务管所有"比"微服务美学"更重要。
给非程序员的技术决策
John的身份让这个项目有点特殊。他没有"必须用K8s"的职业惯性,也没有"这不够scalable"的同行压力。他的决策标准很直接:这周能跑起来吗?能省40%吗?
结果是他用SQLite而不是Redis,用精确hash而不是语义匹配,用单服务而不是拆分。每个选择在架构论坛里都可能被喷,但账单上的数字替他辩护。
他也提到了边界:如果请求量再涨10倍,SQLite会成为瓶颈。但那时候,他有省下的$400/月来付Redis托管费。过早优化是罪恶,但过晚优化可以用钱解决。
John把代码开源了。评论区有人问:为什么不用Vercel AI SDK的缓存?他回复:那个需要改应用代码,他的方案是零侵入——改个API endpoint就行。
产品思维和技术选型的交叉点,往往不在"最先进",而在"最少改动"。
你现在每月给OpenAI多少钱?如果插一层缓存能省40%,但需要你维护一个Node.js服务,这笔账你会怎么算?
热门跟贴