Day 09 / 共 20 天 · 第 2 周 Block 系统

Block 成本计量

AutoGPT Platform 是计量收费 SaaS——每个 Block 执行都扣 credit。今天讲 Block 怎么定价:六种成本类型、静态/动态、按参数定价。

📍 你在整门课的位置 · 第 2 周 Block 深入
D8 Block 凭证 D9 Block 成本 D10 SDK D11 Graph 模型深入
💡 今天的类比世界观:Block 计费 = 各种"计价方式" 每个块执行都扣 credit,就像日常消费的各种计价:静态成本 = 固定票价(一口价);按次 = 进地铁刷一次扣一次动态成本 cost_filter = 打车按里程 / 车型变价(参数不同价不同);向上取整 = 不足 1 元按 1 元收,宁可多收零头也不漏费。今天都用"计价表"来想。

👶 小白:一个块不应该是固定价吗?为什么价格还能随参数变?

👨‍🏫 老师:因为同一个"调 LLM 块",你选 GPT-4 和选便宜小模型,平台付给上游的钱差好几倍,成本必须跟着变。cost_filter 就是"当参数满足某条件时用这个价"的匹配规则——像打车"选豪华车型单价更高"。所以定价能精确到"用哪个模型、跑多少量",而不是一刀切,既不亏本也不乱收。

L01

credit 钱包

平台内部货币叫 credit(积分)1 credit = 1 美分。用户有余额,每次 Block 执行按价格表扣费,用 Stripe 充值(Day 16)。

为什么要计量收费?(呼应 Day 01 转型) 因为平台替用户真金白银地调用付费 API(OpenAI、各种第三方服务都要钱)。如果不计费,用户随便跑,平台就得倒贴。把每个 Block 的成本明码标价、按用量扣用户的 credit——这是 SaaS 化的商业地基。这也是它和经典 AutoGPT(单机、用你自己的 key)的关键区别:平台可以托管 key、替你付费、再按 credit 向你收费(加一点毛利)。
L02

BlockCost 六种类型

BlockCostTypeblocks/_base.py:99)——一个 Block 可以按六种方式之一计价:

类型怎么算
RUN每次运行固定扣 N 分普通块
BYTE按输入字节数处理大文件
SECOND按运行秒数(N分/M秒)E2B 代码执行
ITEMS按返回条目数Apollo 搜索
COST_USD按供应商实际美元花费折算Jina 网页搜索
TOKENS按 LLM token 数查表LLM 块
为什么要这么多计价方式? 因为不同 Block 的真实成本结构不同:LLM 按 token 收费、代码沙箱按运行时间收费、搜索 API 按调用次数或返回条数收费、大文件处理成本和字节数相关。用统一的"每次固定价"会不公平(跑 1 秒和跑 10 分钟一个价?)。六种类型让每个 Block 都能按它真实的成本结构定价——用得多付得多,公平且可持续。
L03

静态 vs 动态成本

🤔 痛点:LLM 块不跑完根本不知道花多少 token,怎么先扣费防欠费? 如果等执行完再扣费,用户可能余额早已见底却已经烧掉了平台的真金白银;可执行前又确实算不准要用几个 token、跑几秒。这笔账到底什么时候结?
💡 本质:算得准的先扣死,算不准的"先估扣、后对账" 成本分两类:静态(跑一次固定价、字节数已知)执行前一次算准;动态(按秒/条目/token)执行前用历史均值预扣、执行后按真实用量结算多退少补。这就是 Day 05"预扣+对账"两阶段计费的根源。

六种类型分两组(_base.py:113):

  • 静态成本RUN/BYTE):执行前就能算准(跑一次就是固定价、字节数已知)。
  • 动态成本SECOND/ITEMS/COST_USD/TOKENS):执行后才知道(不跑完不知道用了几秒、几个 token)。is_dynamic=True
时间轴 → ① pre-flight 预扣 按历史均值估:先扣 20 credit ② run() 执行中 真实用掉 1730 token ③ post-flight 结算 真实 17 credit → 退回 3
动态成本的两阶段:执行前按历史均值预扣一笔(防欠费),执行后拿真实用量结算、多退少补。静态成本则跳过这套,执行前一次算准即可。
动态成本怎么办?(Day 05 的预扣+对账在这兑现) 动态成本执行前算不准,所以:执行前用历史平均值估算预扣一笔(pre-flight),执行后用真实统计结算(post-flight),多退少补。比如 LLM 块:执行前按该模型的历史平均 token 预扣,执行后按真实 token 数对账。这就是 Day 05 讲的"预扣+对账"的根源——因为动态成本天生算不准,必须两阶段计费。静态成本则执行前一次算准即可。
L04

cost_filter:按参数定价

BlockCost 有个 cost_filter——让"同一个 Block、不同输入参数"能有不同价格。看真实例子(block_cost_config.py:580):

SearchTheWebBlock: [
    BlockCost(cost_amount=150, cost_type=BlockCostType.COST_USD,
              cost_filter={"credentials": {"id": jina_credentials.id, ...}}),
]
# Ideogram:V2 收 12 分、V3 收 14 分(block_cost_config.py:607)——靠 cost_filter 按 model 区分
📝 具体例子:同一个块,输入不同价格不同 Ideogram 生图块挂两条价规则:cost_filter={"model":"V2"} → 12 分cost_filter={"model":"V3"} → 14 分
执行时输入是 {"model":"V3", "prompt":"一只猫"} → 引擎逐条子集匹配:{"model":"V3"} ⊆ 输入 ✓ → 命中第二条 → 本次扣 14 分。若输入是 V2 则扣 12 分。一个块、一份代码,靠 cost_filter 就区分了不同档位的价。
读法:cost_filter 是"子集匹配"条件——只有当 Block 的输入包含这些键值时,这条价格才生效。于是同一个"生成图片"块,用 V2 模型收 12 分、V3 收 14 分——靠 cost_filter 匹配 model 字段来区分。匹配逻辑在 executor/utils.py:286_is_cost_filter_match(递归子集匹配)。
为什么需要"按参数定价"? 一个 LLM 块能选不同模型(GPT-4 贵、mini 便宜),一个图像块能选不同画质档。它们是同一个 Block,但成本因参数而异。cost_filter 让一个 Block 挂多条价格规则,执行时根据实际输入参数匹配到对应的价。灵活精准——不用为每个模型/档位造一个单独的 Block。
L05

成本总表 BLOCK_COSTS

BLOCK_COSTSblock_cost_config.py:535)是总表:dict[Block类, list[BlockCost]]。相关的还有 MODEL_COST(每模型每次扁平价)、TOKEN_COST(每模型每百万 token 费率)、LLM_COST(按 provider 批量生成)。

token 计费的细节compute_token_credits:319):post-flight 用真实 token 数 × 费率;pre-flight 或未登记模型则回落到扁平 MODEL_COST。费率注释说明是在供应商实际价基础上加 1.5x margin(1 credit ≈ $0.01)——平台的毛利来源。⚠️ 一个坑没登记在 BLOCK_COSTS 里的 Block = 免费(从钱包视角,if not block_costs: return 0)。加新块别忘了配成本。
L06

向上取整防漏费

block_usage_cost()executor/utils.py:123)算成本时,SECOND/ITEMSmath.ceil(units/divisor)*amount——向上取整utils.py:174)。

为什么向上取整? 假设定价"每 10 秒 1 credit"。如果一个执行跑了 3 秒——向下取整就是 0 credit(不足一档 = 免费),用户可以疯狂跑几秒的任务白嫖。向上取整 = "不足 10 秒也算 1 档,收 1 credit"。防止"不足一档 = 0 收费"的漏费漏洞。这种对计费边界的严谨,是计量 SaaS 的钱袋子保护——差之毫厘,规模化后就是大漏洞。
L07

成本估算校准

动态成本 pre-flight 要给个"估算值",从哪来?block_cost_analytics.pycompute_block_cost_estimates:66)——跑 SQL 聚合过去最多 90 天的真实扣费记录,算出每个 Block 的 mean/p50/p95,生成 block_preflight_estimates.json

这是什么运维智慧? 动态成本块执行前显示"预计花费",如果一律显示 0(因为算不准),用户体验差、也没预扣保护。用历史真实数据算出"这个块平均花多少",作为 pre-flight 估算——执行前显示合理预估、预扣合理金额,执行后只结算小差额。这个分析是离线校准(不参与实时计费),定期跑,让估算越来越准。"用历史数据校准估算"——数据驱动的成本预测。
L08

今日小结 + 动手

🧠 今天你应该能回答

  • credit 是什么?为什么要计量收费?
  • BlockCost 六种类型?为什么要这么多?
  • 静态 vs 动态成本的区别?动态为什么要预扣+对账?
  • cost_filter 怎么实现"按参数定价"?
  • 为什么向上取整?成本估算怎么校准?

✋ 动手

P=autogpt_platform/backend/backend
sed -n '99,158p' $P/blocks/_base.py                  # BlockCostType + BlockCost
sed -n '535,620p' $P/data/block_cost_config.py | head -40   # BLOCK_COSTS
sed -n '123,210p' $P/executor/utils.py | head -50    # block_usage_cost + 向上取整
明天预告 · Day 10(第2周收官)SDK 与自定义 Block——开发者如何用 SDK 写一个新 Block、ProviderBuilder 声明第三方集成、AutoRegistry 自动注册。收官 Block 系统。
← Day 08 凭证 Day 10 · SDK →