4.1 中小团队与成长型产品:两个轻量降本案例 很多人下意识觉得「降本」是大厂的专利——毕竟只有大厂才有百万级调用、才养得起专门的平台团队。这其实是个误区。恰恰相反,中小团队做降本的投入产出比往往最高:没有历史包袱、技术债少、决策链路短,一个工程师花一周搭起的缓存加路由,可能直接把月度账单砍掉四成。 本章不讲"应该怎么做"的抽象原则,而是把镜头拉到两个真实体量不同的团队,看他们到底踩了哪些坑、做了哪些取舍、最后省下了多少。这两个案例覆盖了日调用 3 万次到 20 万次这一最普遍的中小到成长区间,是绝大多数产品都会经历的阶段。 案例一:10 人创业团队,一周搭起缓存加路由 起点:账单在涨,但没人说得清钱花哪了 这家团队做垂直行业的 SaaS,产品里嵌了一个客服问答机器人。
很多人下意识觉得「降本」是大厂的专利——毕竟只有大厂才有百万级调用、才养得起专门的平台团队。这其实是个误区。恰恰相反,中小团队做降本的投入产出比往往最高:没有历史包袱、技术债少、决策链路短,一个工程师花一周搭起的缓存加路由,可能直接把月度账单砍掉四成。
本章不讲"应该怎么做"的抽象原则,而是把镜头拉到两个真实体量不同的团队,看他们到底踩了哪些坑、做了哪些取舍、最后省下了多少。这两个案例覆盖了日调用 3 万次到 20 万次这一最普遍的中小到成长区间,是绝大多数产品都会经历的阶段。
这家团队做垂直行业的 SaaS,产品里嵌了一个客服问答机器人。日调用约 3 万次,月 Token 账单稳定在 2000 美元左右,且随着用户增长稳步爬升。创始人第一次认真看账单时,发现一个反直觉的事实:每天有大量请求,问的是几乎一模一样的问题——"怎么重置密码""支持哪些文件格式""你们的价格方案是什么"。
更糟的是,为了"让回答更准",他们把整个知识库每次都塞进系统提示词,导致每一个请求无论问题多简单,输入 Token 都居高不下。也就是说,钱不仅花在"重复回答"上,还花在"每次都重新搬运同样的背景知识"上。
我们把他们的请求日志拉出来做了分层统计,定位出三块最肥的浪费:
这三类,前两类靠缓存解决,第三类靠路由和提示词裁剪解决。诊断这一步只花了一个下午,却是整个改造的支点——没做诊断就动手优化,等于蒙着眼睛砍预算。
他们没动原有业务代码,而是在模型调用前插了一个薄薄的代理层:
第一步,上精确缓存。 对"问题原文 + 关键参数"做哈希作为键,命中则直接返回上次答案。这一步当天就上线,吃掉了那 12% 的完全重复请求。注意,缓存键里剔除了会话 ID、时间戳这类易变字段,只哈希语义核心。
第二步,上加语义缓存。 用 embedding 模型把问题向量化,存进向量库;新请求先算相似度,超过阈值(他们设 0.92)就返回最相似历史问题的答案。这一步啃下了那 23% 的近似提问。这里有个关键取舍:阈值不能设太高也不能太低。太高(如 0.98)几乎等于精确缓存,失去意义;太低(如 0.8)会返回答非所问的结果,伤害体验。他们通过一周的抽样人工校验,把阈值定在 0.92。
第三步,上智能路由。 用一个极轻量的分类器(其实就是几个规则加一个小模型)判断问题难度:高频 FAQ、格式说明类走便宜的小模型;需要推理、跨文档综合的才走大模型。这一步把约 40% 的请求导流到低成本模型上。
第四步,提示词裁剪。 不再每次塞整库,而是先用检索(RAG)只取和问题相关的片段,再拼进提示词。输入 Token 直接降到原来的三分之一。
代理层的缓存键归一化伪代码大致长这样,读者可以直接复用思路:
import hashlib, re def cache_key(question: str, doc_version: str, lang: str) -> str: # 只保留语义核心字段,剔除会话id/时间戳等易变字段 core = f"{doc_version}|{lang}|{normalize(question)}" return hashlib.sha256(core.encode("utf-8")).hexdigest() def normalize(text: str) -> str: # 去除空白与标点扰动,让"你们多少钱"和"你们 多少钱?"趋同 return re.sub(r"\s+|[??!!。.]+", "", text).lower()
这段代码里的 normalize 很关键:它把"你们多少钱"和"你们 多少钱?"归一为同一字符串,相当于在精确缓存之前先做一次轻量语义清洗,能把精确命中率再抬几个点。
上线一个月后的数据:
工程师实际投入约 5 个工作日。如果按省下的钱算,这个改造在第一周就回本了。
这里我想替读者下一个判断:中小团队最容易忽略的不是"用什么高级方案",而是"先别急着重构"。 在调用入口插一个薄代理层,用缓存加路由把最肥的浪费先吃掉,性价比远高于推倒重来一套所谓"平台"。
第二个团队产品更成熟,是一个面向个人的 AI 写作助手,日调用约 20 万次。他们比第一个团队早半年就上了缓存,但命中率长期停在 15% 左右,几乎没带来什么账单改善。技术负责人一度怀疑"语义缓存是不是在咱们场景就是没用"。
我们接手做诊断后才发现,问题不在语义缓存这个思路,而在三个具体实现上的低级错误。
他们的缓存键是"完整请求体 JSON 的哈希",而请求体里混入了时间戳、随机请求 ID、会话状态。结果就是同一个问题,因为带了不同的时间戳,算出来的键永远不一样,精确缓存形同虚设。语义缓存虽然能兜一部分,但被这批"伪不同"请求稀释了向量库质量。
修复方式很简单却很关键:定义一套"语义核心字段白名单",只把真正决定答案的字段(问题文本、文档版本号、用户语言)纳入哈希,其余一律剥离。这一改,精确缓存立刻从近乎失效恢复到正常水平。
他们给所有缓存统一设了 1 小时 TTL。问题在于,写作助手里大量内容是"静态知识型"回答——比如语法规则、格式模板、常见修辞手法,这类内容几周都不会变,1 小时就过期意味着第二天同一问题又得重新算。
改成按内容类型分档 TTL:静态知识型 7 天,半动态(如结合热点的写作建议)12 小时,强时效(如引用当天新闻)不缓存。这一改,语义缓存的有效命中窗口被大幅拉长。
TTL 分档用一个简单的元数据标签就能落地,伪代码思路:
TTL_POLICY = { "static": 604800, # 7天:语法、模板、修辞 "semi": 43200, # 12小时:热点建议 "realtime": 0, # 不缓存:当日新闻 } def pick_ttl(content_type: str) -> int: return TTL_POLICY.get(content_type, 43200)
他们最初用的是维度较低的轻量 embedding 模型,好处是快、便宜,坏处是区分度不够。比如"写一封辞职信"和"写一封感谢信"在向量空间里靠得太近,导致语义缓存经常把不该复用的答案复用出去,为了不犯这种错,他们被迫把相似度阈值设得很高,结果大量本可命中的请求被拒之门外。
升级到区分度更高的 embedding 模型后,向量空间里不同意图拉开了距离,阈值可以放心下调,命中率随之上升,且没出现"串味"导致的错误复用。
诊断中我们还发现一类特殊浪费:对明确"我不知道/无法回答"的请求,模型也会老老实实生成一段拒答,每次都花 Token。我们加了一条负缓存规则——对这类确定性拒答也做缓存,下次同类问题直接返回,不再进模型。这属于"边角料"优化,但积少成多,在 20 万日调用的体量下每月也能省下几百美元。
三处修复上线后:
这个案例最值得读者带走的结论是:命中率低,十有八九不是"缓存没用",而是"缓存键、TTL、embedding 这三件套没调对"。 在动手怀疑方案之前,先拿请求日志做一次分层诊断,往往能省下大把冤枉钱。
把这两个案例放在一起看,你会发现它们走的其实是同一条路,只是起点不同:
我见过不少团队把两件事搅在一起,以为"上了缓存就不用管路由"。这里必须把账算清楚:
两者是正交的,可以叠加。这就是为什么案例一里既上缓存又上路由,账单能砍 45%——单做其一都到不了这个幅度。读者在写技术方案时,建议把这两笔账分开估算,再合并汇报,避免被"我们命中率 35%"一个数字迷惑,以为降本就到此为止。
走过这两个案例,我把最容易被忽视、却最能决定成败的三条写在这里,供你直接对照执行:
下一节我们把镜头拉到更上游:当日调用冲到百万级、多个业务线共用一套模型网关时,单点插代理层就不够了,需要一套平台级的成本治理架构。那是另一个量级的玩法。