商略AI

AI SaaSの原価は、実消費と月間上限の両方で管理する

更新 一次調査10分

AI SaaSの原価は、モデルの1回あたり単価を計算するだけでは管理できません。 1回ごとの実消費を記録しても、利用回数に上限がなければ月間原価は決まりません。逆に月間クレジットを置いても、実消費と精算していなければ上限として機能しません。

自分は後者を踏みました。分析1回を2,000クレジットの定額にしていましたが、現在の計算では典型的な1回が94,400クレジットです。予約額と実消費に47倍の差がありました。

個人開発でマーケティング支援SaaS「商略AI」( https://shoryaku.net )を作っています。推論は Vertex AI の Gemini、決済は Stripe、インフラは Firebase です。現在は、モデルの実単価から1回ごとの消費を出し、月額売上の25%をAI原価の上限にしています。

先に現在の試算

backend/scripts/plan-economics.ts を実行した結果です。料金を下げたため、値は公開時から変わっています(経緯は後述)。

プラン 月額 主な分析モデル 典型時のAI原価 AI原価の月間上限 AI原価とStripe手数料を引いた残り率
ライト ¥980 Gemini 2.5 Flash ¥53 ¥244 典型91% / 上限71%
スタンダード ¥2,980 Gemini 2.5 Flash ¥212 ¥745 典型89% / 上限71%
プロ ¥4,980 Gemini 2.5 Pro ¥1,069 ¥1,244 典型75% / 上限71%

ここでいう「典型」は実利用の平均ではありません。有料顧客がまだいないため、トークン数は実装から置いた試算です。

試算プロファイル 入力 出力(思考を含む) 1分析あたりの呼び出し数
典型 30,000 20,000 1.2回
重い 60,000 50,000 2回

単価、モデルの割り当て、プラン上限はコードの実値です。トークン量だけが見積もりなので、上の表は実測原価ではなく、現行実装による原価試算として読んでください。

Firebaseの使用量は別に実測しており、26日間に比較できた項目は無料枠の内側でした。この記事では、利用量に応じて増えるAI原価に絞ります。

1回の原価は、思考トークンまで含めて出す

現在の原価計算は、Vertex AI が返す使用量を次の4種類に分けます。

使用量 原価での扱い
通常の入力トークン 入力単価
キャッシュ済み入力 キャッシュ単価
ツール利用用の入力 入力単価
出力 + 思考トークン 出力単価

実装の中心は次の行です。

const outputTokens = tokenCount(usage.outputTokens) + tokenCount(usage.thoughtTokens);

Geminiの思考トークンは、利用者へ見える回答本文とは別に消費されます。ここを出力へ足さないと、回答が短い処理ほど原価を小さく見積もります。

実際にルーティングしているモデルの global 料金は、コード上で次のように管理しています。

モデル 入力 / 100万 キャッシュ入力 / 100万 出力 / 100万
Gemini 2.5 Pro $1.25 $0.125 $10.00
Gemini 2.5 Flash $0.30 $0.03 $2.50
Gemini 2.5 Flash-Lite $0.10 $0.01 $0.40

モデルごとに「出力 ÷ 入力」の比率が違うため、入力1・出力5のような固定重みを原価計算には使いません。

旧クレジット制は、月間原価を縛っていなかった

公開時の実装では、分析を始める前に2,000クレジットを予約し、完了後の実消費と精算していませんでした。

現行の試算で比べるとこうなります。

分析1回 旧予約額 現在の実消費換算 差
典型 2,000 94,400 47倍
重い 2,000 381,334 191倍

当時のスタンダード上限は100,000クレジットでした。実消費で数えるなら、典型的な分析1回でほぼ使い切る量です。それでも30回動かせていたのは、クレジットが安かったのではなく、実消費を台帳に計上していなかったからでした。

現在は、実行前の予約を最低額として残し、完了後に差額を追加計上します。

const overage = actual - ANALYSIS_CREDIT_RESERVE;

予約は同時実行で上限を超えないために必要ですが、原価の正本ではありません。原価の正本は、完了後に返ったモデル・入出力・思考トークンです。

1クレジットを、モデルではなく金額に結びつけた

固定の「入力1 / 出力5」をやめ、現在は1クレジットを 0.75 microsUSD と定義しています。

export const CREDIT_MICROS_USD = 0.75;

1回の消費は、モデル別の実原価をこの金額で割って切り上げます。

return Math.ceil(micros / CREDIT_MICROS_USD);

この形なら、高いモデルへ切り替えると同じトークン数でもクレジット消費が増えます。月間上限はそのまま原価の上限になります。

プラン 月間クレジット 1クレジット AI原価の上限(1ドル150円換算)
ライト 2,170,000 0.75 microsUSD ¥244
スタンダード 6,620,000 0.75 microsUSD ¥745
プロ 11,060,000 0.75 microsUSD ¥1,244

いずれも月額売上の約25%です。重い分析が続けば、表示上の分析回数を使い切る前にクレジットが尽きます。分析回数は保証ではなく、クレジット残量内での上限として表示しています。

モデルを変えるときに壊れた3点

1. モデルごとに単価比が違う

Gemini 2.5 Proは出力が入力の8倍、Flashは約8.3倍、Flash-Liteは4倍です。固定重みは、モデルを切り替えた時点で実原価とずれます。

2. 価格表にないモデルは0円に見える

モデル名は文字列なので、存在しないIDや価格表に未登録のIDも型チェックを通ります。以前、設定に存在しないモデルIDが入りました。本番の既定値では使われませんでしたが、切り替えると失敗する状態でした。

現在は、ルーティング対象の全モデルが価格表にあるかを起動時に検査します。未登録モデルの呼び出しは0円ではなく、現在使う中で最も高いモデルの単価で仮計上します。安く見せて見逃すより、多めに計上して検知するためです。

3. 現在の単価だけでは将来を見積もれない

価格表には確認日を持たせています。告知済みの価格改定があるモデルは、適用日以降の単価も別に持ちます。「LLMの価格は下がり続ける」と前提に置かず、いつの価格かをコードへ残すようにしました。

台帳と請求を別の式で計算すると、またずれる

47倍のずれを直したあとにも、もう1つ経路が残っていました。原価台帳とクレジット消費を、別々の式で計算していたことです。

施策生成は、完了後に返った呼び出し一覧(実モデル・入力・キャッシュ・出力・思考トークン)から原価台帳とクレジットの両方を出していました。ところが、チャットと記事の下書き生成は、入力トークンと出力トークンの合計だけでクレジットを計算する古い糖衣関数を通っていました。

この経路で落ちていたものは3つです。

落ちていたもの 何が起きるか
思考トークン Vertex AI は思考を出力として課金する。回答の質を上げるルートは出力上限の4割まで思考に使う設定なので、回答本文が短い処理ほど消費の大半がクレジットに載らない
キャッシュ割引 キャッシュ済み入力を通常の入力単価で数える。こちらは多めに取る方向のずれ
モデルの切り替え 混雑(429)で Pro から Flash へ切り替えても、呼び出し前に決めたモデルの単価で数える

さらに、質問の種類を判定する分類器は、モデル名の欄に実モデルではなくルート名の "fast" を残していました。価格表に無い名前なので、前の節で書いた「未登録は最も高い単価で仮計上する」規則が働き、一番安い処理を Pro の単価で請求する状態でした。安く見せない方向に倒した規則が、今度は取りすぎる方向で顔を出した形です。

直し方は、式を1つにすることでした。

export function creditsForModelCalls(calls: ReadonlyArray<AiModelCallUsage>): number {
  return calls.reduce((sum, call) => sum + creditsForUsage(call.model, call), 0);
}

施策生成、チャット、下書き生成のどれも、台帳に載せるのと同じ呼び出し一覧をこの関数へ渡します。呼び出しごとに実モデルの単価で計算して切り上げるので、1ターンの中で Pro と Flash が混ざっても崩れません。下書き生成の呼び出しは、それまで台帳に載っていなかったので、content_generation として記録するようにしました。

入出力の合計だけを受け取る糖衣関数は残していますが、思考トークンを持つ呼び出しには使わないとコメントで縛っています。原価の正本を「完了後の呼び出し一覧」と決めたなら、請求もそこから出す以外の経路を残さないことが、47倍のずれを直したときに学んだはずのことでした。

公開時の結論を訂正する

公開時は、スタンダードのAI原価を月140円、原価合計を340円、粗利率を約92%と書きました。現在の実装と同じ条件ではありません。

下の表の金額は、当時のスタンダード(月額4,980円)に対するものです。現在の金額は「先に現在の試算」を見てください。

公開時に書いたこと 訂正(当時の月額4,980円に対して)
AI原価は月140円 典型試算は月319円、上限は1,238円
AI原価よりStripe手数料が高い 典型試算ではAI原価319円、Stripe手数料約179円でAI側が高い
粗利率は約92% AI原価とStripe手数料を引いた残り率は典型90%、上限72%
固定の単価比でクレジットを計算できる モデル別の実原価を金額単位のクレジットへ換算する
分析1回は2,000クレジット 2,000は予約額。完了後に実消費との差額を精算する

誤りはモデル単価の計算より手前にありました。 実消費と精算していない経路が1つ残っていれば、料金表が正しくても月間原価は管理できません。

値下げしても、比率は動かさなかった

公開から8日後に料金を下げました。スタンダードは4,980円から2,980円、プロは9,800円から4,980円、下に980円のライトを足して3段にしています。

このときAI原価の上限を売上の25%に置く比率は変えませんでした。 比率で決めているので、価格を下げると上限も自動的に下がります。

旧スタンダード ¥4,980 新スタンダード ¥2,980
月間クレジット 11,000,000 6,620,000
AI原価の上限 ¥1,238 ¥745
売上に対する比率 25% 25%
上限まで使われたときの残り率 72% 71%

金額を先に決めて後から上限を当てはめるのではなく、比率で決めておくと価格改定で原価設計を組み直さずに済みます。 ここは値下げして初めて確かめられた点です。

ただし比率には床がありました。22%を下回ると、画面に表示している分析回数へ届く前にクレジットが尽きます。 重い分析が続く場合の1回あたりクレジット消費に、表示回数を掛けた値が上限を超えるためです。「今月の分析回数を使い切りました」は説明できますが、「クレジットが足りません」は説明しにくいので、表示する回数のほうを実際に到達できる数字へ寄せました(プロは当初案の30回から25回へ)。この条件はテストで固定しています。

値下げの理由は原価ではありません。獲得を対話ありの直接販売から検索流入中心へ移したため、価格が説明役を全部引き受けることになったからです。原価は制約になっていませんでした。価格そのものをどう判断したかはSaaS月額4,980円は安すぎるのか。競合10社とCACで検証に書いています。

原価上限があっても、事業が成立するとは限らない

AI原価を売上の25%以内に収めても、顧客獲得コストを回収できるとは限りません。価格、解約率、有料転換率が未計測なら、原価以外のほうが不確実です。

自社の仮定を使ったGoogle広告のCAC試算では、当時の月額4,980円に対して回収19か月でした。この19か月も実績ではなく仮定です。値下げ後は同じ仮定なら回収がさらに延びます。 原価の上限を守れることと、獲得費用を回収できることは別だという例です。

原価設計で確認できるのは、使われたときに赤字が青天井にならないことまでです。売れるか、継続するかは別に測る必要があります。

まとめ

  • 1回の原価は、入力・キャッシュ・ツール利用・出力・思考トークンをモデル別単価で計算する
  • 実行前の予約額だけで終わらせず、完了後に実消費との差額を精算する
  • 1クレジットを金額に結びつけ、月間上限をそのままAI原価の上限にする
  • AI原価の上限は3段とも売上の25%。現行試算で典型53〜1,069円、上限244〜1,244円。実利用平均は未計測
  • 上限を金額ではなく比率で決めておくと、値下げしても原価設計を組み直さずに済む
  • 公開時の月140円・粗利92%は、実消費を計上していない経路があったため訂正した
  • 原価台帳とクレジットは同じ呼び出し一覧から1つの式で出す。別の式が残ると、思考トークンとモデル切替が請求から抜ける
  • 原価上限は赤字の防波堤であり、顧客獲得や継続の成立を保証しない