前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
返回 AI 情报前线
All News · 全部资讯9275
  • 缓存贵的、重算便宜的:LLM输出缓存策略
  • Agent工具调用的安全防护要点
  • 搜索延迟拆解:毫秒级预算分配实战
  • 推理模型延迟预算分析
  • 推理 Effort 档位实际控制什么
  • 推理模型性价比曲线:如何算清每分正确率成本
  • 为什么推理能力在数学好过代码好过文本
  • 通过所有测试的代码仍可能让AI代理崩溃
  • Cursor 修 IDOR 易漏同文件其他路由
  • 可信数据是AI Agent规模化的关键瓶颈
  • Anthropic为Agent引入梦境推理能力引热议
  • Python 中缓存 LLM 响应:细节决定成败
  • Harness Engineering:打造 AI 编程助手的工作环境
  • 加向量数据库前,先用 NumPy 搞定 Embedding
  • 自实现令牌桶限流:比等 429 更优
  • 长时 AI 任务用后台任务队列:状态机比框架重要
  • asyncio 并发调用 40 个 LLM:代码少但坑多
  • AI 项目中 API 密钥的泄密风险与防护层级
  • 电话Agent工程实践:SIP/WebRTC音频路径全解析
  • pgvector索引调优:HNSW与IVFFlat成本精确分析
  • 多租户应用按客户计费:成本与可计费用量必须分开
  • PDF 解析难点深度剖析:字符间距阈值是万恶之源
  • Hugging Face开源OlmoEarth文本嵌入
  • 阿里开源 Qwen3.8:2.4T MoE、激活 95B、256K 上下文
  • MiniMax H3:单个 Transformer 替代视频生成完整流水线
  • DeepSeek V4 Pro 正式版发布:多项测试接近 Fable 5 水平
  • AI编程助手Lovable完成新一轮4亿美元融资,估值达133亿美元
  • MiniMax Music 3.0:开放权重生产级音乐生成模型
  • Microsoft 发布 MindTopo:VLMs 空间推理能力新基准
  • Grok 4.6 发布:剑指 GPT-5.6 Sol,主打长时间 Agent 任务
  • Grok 4.6 中文详解:训练数据、Agent 能力边界与定价
  • 市场份额报告:Google Gemini 份额从 12% 跌至 1.9%
  • 用Embeddings+Reranking+LLM构建可靠的内容分类流水线
  • 推理冷启动从10分钟降至秒级:容器镜像瘦身实战
  • Anthropic研究揭示:Claude Code旧版权限提示97%被机械通过
  • DeepSeek-V4-Pro-0813 悄然上线,支持思考与非思考模式
  • AI 生图 Prompt 审核实战:Node.js 调用 Chat JSON Schema 方案
  • 企业AI分析的隐藏陷阱:语义漂移问题深度剖析
  • AI 正在消除软件工程中层:代码看不懂、没人负责的团队困境
  • Qwen3.8-2.4T-A95B 模型发布
  • TraceMotive:本地优先的AI代理执行追踪调试工具
  • AI编程工具正在离开IDE:终端原生Agent工作流崛起
  • 个人开发者用AI编程的项目架构经验
  • FastAPI五个安全漏洞发现与修复全过程
  • 长文档AI审核的审计设计:Map-Reduce优于检索增强
  • AI Agent辅助发现SharePoint RCE漏洞链(CVSS 9.1)
  • 我用Claude Code将API的P99延迟降低一半
  • AI Agent读了你的secrets并删了生产数据库——PocketOS事故详解
  • 用聊天模型做金融内容审核:结构化输出设计实践
  • Prompt注入攻击原理与防御实践指南
  • 大规模漏洞扫描活动泛滥,攻击者冒充ClaudeBot等AI爬虫
  • 已加载 51 / 9275
8.0
热点
AI SCORE
技术实践2026-08-13 00:30

多租户应用按客户计费:成本与可计费用量必须分开

dev.to · AI#多租户#计费系统#架构设计
Editor brief · 编辑速览

多租户计费要把「实际云成本」和「客户套餐消耗」拆成两个独立字段,否则续约谈判时一个值变另一个也跟着变,导致计费逻辑被污染。

文章思维导图
Knowledge map
拖拽缩放
Full translation

完整中文译文

成本与可计费用量是不同的数字

将两者混为一谈是大多数按租户计费痛苦的根源。它们回答的是不同的问题,各自独立变动,应该作为独立的列。

在同一行保留两个字段。cost_usd 是你实际支付的费用;billable_units 是你将用于收费的数量,由一个带版本号的函数计算。当你和提供商重新谈判合同时,cost_usd 下降而 billable_units 不变——这正是你想要的行为,而如果你只有一个列,这就无法实现。

共享成本问题

并非每个美元都归属于某一个租户。在几乎每个多租户 LLM 产品中都会出现三种情况,每种都需要明确声明策略,而不是依赖 group-by 碰巧得到的结果:

缓存的共享前缀。如果一个长的 system prompt 被缓存并在租户之间重用,发出请求填充缓存的租户支付了全价,之后的所有人享受缓存价格。按字面意思计费意味着一个客户在补贴其他人。要么向所有人收取混合费率并吸收差异,要么按未缓存的标价计费并将缓存视为你的利润——两种都是合理的选择,默默地因为偶然做了第一种是不可接受的。

共享语料 embedding。对所有租户都可以搜索的文档集进行 embedding 是平台成本。将其标记为 tenant_id = null,feature = 'platform',完全将其排除在按租户的统计之外。

重试和失败。一个失败后重试的请求花费了你双倍成本但只交付了一个答案。cost_usd 记录两次尝试;billable_units 只记录一次。这就是为什么每条记录应该记一次尝试而不是一个逻辑调用的具体原因。

汇总表,以及为什么它不是一个视图

对账单周期直接查询原始请求日志在日志规模较小时可行,之后就会变得缓慢且不一致——同一查询相隔几分钟的两次运行会返回不同的数字,因为记录还在不断涌入。计费需要一个在账期关闭后稳定的数字。

create table llm_usage_daily (
  tenant_id      text        not null,
  day            date        not null,
  feature        text        not null,
  model          text        not null,
  requests       bigint      not null,
  input_tokens   bigint      not null,
  output_tokens  bigint      not null,
  cached_tokens  bigint      not null,
  cost_usd       numeric(14,6) not null,
  billable_units numeric(14,4) not null,
  computed_at    timestamptz not null default now(),
  source_max_id  uuid,          -- high-water mark of rows folded in
  revision       integer     not null default 1,
  primary key (tenant_id, day, feature, model)
);

-- Recompute one day, idempotently. Safe to run any number of times.
insert into llm_usage_daily as u
  (tenant_id, day, feature, model, requests, input_tokens, output_tokens,
   cached_tokens, cost_usd, billable_units)
select tenant_id,
       started_at::date,
       feature,
       served_model,
       count(*) filter (where attempt = 1),          -- logical calls
       sum(input_tokens),
       sum(output_tokens),
       sum(cached_input_tokens),
       sum(cost_usd),                                -- every attempt costs
       sum(billable_units) filter (where attempt = 1)
from llm_request
where environment = 'prod'
  and tenant_id is not null
  and started_at >= $1::date
  and started_at <  $1::date + 1
group by 1, 2, 3, 4
on conflict (tenant_id, day, feature, model) do update
set requests       = excluded.requests,
    input_tokens   = excluded.input_tokens,
    output_tokens  = excluded.output_tokens,
    cached_tokens  = excluded.cached_tokens,
    cost_usd       = excluded.cost_usd,
    billable_units = excluded.billable_units,
    computed_at    = now(),
    revision       = u.revision + 1;

三个属性使其可以安全地从 cron 任务以至少一次交付语义运行。它是有 key 的,所以重复运行会覆盖而不是翻倍。它限定在一天范围内,所以回填就是一个循环。它携带 revision,所以"这个数字在我们展示给客户之后发生了变化"是可以检测到的而不是神秘的。

晚到的记录、修正和幂等性

记录会在所属日期之后才到达。一个在 00:00:03 完成的流式响应的时间戳是其开始时间;一个异步 worker 延迟刷新缓冲区;提供商 webhook 重报用量。两条规则处理几乎所有这些情况。

对尾部窗口重新计算,而不只是昨天。每晚重新运行最后三天几乎不花什么代价,能吸收除了真正的重报之外的一切。只有在开票时才冻结一个周期。

向前修正,绝不就地修改。一旦一个周期已开票,发现的错误变成下一期的一条调整记录,带有原因码。编辑已关闭的周期会破坏发票唯一需要具备的东西:从存储数据可重现。

如果你向计量或计费系统发出用量事件,使用 request_id 作为事件的幂等 key,而不是按批次的 id。重试投递是正常的,收到相同 request_id 两次的计量系统应该只记录一次,而双方都不需要为此费心。

在午夜和月末仔细观察边界。涉及两个时钟——请求的开始时间和完成时间——对于一个长流式响应,它们可能落在不同的日期。选择一个并在所有地方使用它:开始时间是更好的选择,因为它在记录写入之前就已知的,不会改变,并且与用户描述他们何时发出请求的方式一致。无论选择哪个,在汇总表、发票和仪表板中使用相同的那个,否则三个略有不同的总数会并存,没有人能说哪个是正确的。

财务实际会问的查询

不是"我们花了多少钱"。问题是哪些客户的成本高于他们支付的费用,这是一个你的用量汇总表和订阅表的 join。

select s.tenant_id,
       s.plan,
       s.mrr_usd,
       round(sum(u.cost_usd), 2)                                 as model_cost,
       round(s.mrr_usd - sum(u.cost_usd), 2)                     as gross_margin,
       round(100 * (1 - sum(u.cost_usd) / nullif(s.mrr_usd, 0)), 1) as margin_pct,
       round(sum(u.cost_usd) / nullif(sum(u.requests), 0), 6)    as cost_per_request
from llm_usage_daily u
join subscription s using (tenant_id)
where u.day >= date_trunc('month', now())::date
group by 1, 2, 3
order by gross_margin asc
limit 25;

升序排列是整个技巧所在:第一页是亏损客户的名单,这是这个报表唯一有人会真正去看的版本。每周跑一次,并将其与分布而不是均值配对——在大多数按用量付费的产品中,少量租户占据了大部分支出,所以平均利润率可能看起来很健康,而少数账户却深度亏损。

在它旁边还有一个值得拥有的配套查询:对已停止使用产品的定费计划租户进行同样的计算。那些账户看起来像纯利润,但通常是流失预警而非成功案例,这提醒我们单独看的成本报告倾向于奖励错误的东西。在同一个视图中将利润率与参与度数字配对,对话会保持诚实。

关于分母的一个注意事项。Cost per request 是一个诱人的单位,也是一个误导的单位,因为一个 request 不是价值单位。在可以定义的地方,优先使用 cost per completed task——每个已解决的工单、每份生成的文档、每个被采纳的建议。那个数字在你改进 prompt 时会变动,而 cost per request 往往不会。

这方面通常比较棘手的是定价表:你需要按提供商维护最新的每模型费率,包括缓存输入费率,否则上述每个数字都会漂移。Multigrid 维护这个目录并在每条请求上标注定价后的成本,这至少给你第二个数字来与你自己的计算进行核对。

Original source

本文由 AI 翻译整理自 dev.to · AI,原文版权归原作者所有。

阅读英文原文
上一篇
pgvector索引调优:HNSW与IVFFlat成本精确分析
下一篇
PDF 解析难点深度剖析:字符间距阈值是万恶之源