E 会計/ファイナンス・コスト効率 — 「会員1人ずつに LLM でクーポン訴求文、ROI 16倍」の稟議を、トークンの単位経済と限界利益で書き直す(出力・思考・キャッシュ最小値・導入価格で 23.4 倍 × A/B p=0.30 × 1クリック 48円 → セグメント×数案から、1対1は Flash-Lite バッチで上積み 0.083pt を測って判断)

2026-10-09 金曜 金曜 E: 会計/ファイナンス・コスト効率 ★★★★★ LLM 単位経済 / Gemini 料金表 限界利益 / A/B の検出力 / 景表法

概要

🧾

料金表は注記まで読む

出力は入力の 5 倍、思考も出力。キャッシュは 4,096 未満で効かない。導入価格は 12/31 まで。

💴

効果は限界利益で

1 クリック 売上 240 円、会社に残るのは 48 円。ROI 16.4 倍は売上で割った数字。

📏

差でなく信頼区間

2,000 通ずつの +0.5pt は p=0.30。見つけるには 1 群 14,406 通。

🧩

比較相手はセグメント案

300 × 4 案なら月 $55。1 対 1 は上積み 0.083pt を回収できるかで決める。

問題

MOps 販促システムチームの後輩が、メールの件名・本文を会員ごとに LLM(Gemini)で生成する稟議を書いた。部長から「数字を見てほしい」と頼まれ、あなたがレビューする。

単価(公式 参照日 2026-10-09 ai.google.dev/gemini-api/docs/pricing、100 万トークンあたり)

モデル入力出力(思考を含む)キャッシュ済み入力保管備考
Gemini 3.8 Flash(〜2026-12-31)$0.75$3.75$0.075$0.50/時2027-01-01 から全項目が倍
Gemini 2.5 Flash-Lite$0.10$0.40$0.01$1.00/時

制約・前提条件

  • バッチは標準の 50%、目標 24 時間 公式。3.x Flash のキャッシュは最小 4,096 トークン 公式
  • 実際のプロンプト 仮定: 共通 3,000/会員ごと 700/出力 150/思考 平均 450。検査の差し戻し 12%
  • 注文 1 件の限界利益 仮定: 8,000 円 × 30% − クーポン 500 − 配送・決済 300。CVR 3%。文面は前日夜に確定。150 円/$

期待する回答形式: 問題点 8 点/費用と効果を数えるコードとテスト/実行例/推奨案と稟議の書き直し/迷った案と前提が変わったときの判断/扱わなかった範囲

悪い稟議 (Before)

この稟議には 8つの問題 が隠れています。見つけてから先へ進んでください。
後輩の稟議

件名: 会員ごとのクーポン訴求文を LLM で生成する(年間 ROI 16.4 倍)

モデル Gemini 3.8 Flash(入力 $0.75/100万)。1 通 = 入力 500+出力 150 = 650 トークン × $0.75 = $0.00049。300 万人 × 月 4 通 = 1,200 万通 → 月 $5,850、年 約 1,053 万円。共通の指示はキャッシュで 90% 引きなので実際はもっと安い。

効果: A/B(2,000 通ずつ)でクリック率 2.1% → 2.6%。1 クリック 240 円の売上 → 月 1,440 万円 → ROI 16.4 倍。11 月から、送信直前にリアルタイム生成。

問題点サマリー(8点)
1-2単価 — 出力を入力単価で/思考トークン未計上
3-4量とキャッシュ — 入力 3,700 を 500 と/最小 4,096 に届かない
5-6時期と作り直し — 導入価格の期限/差し戻し 12%
7-8効果と比較 — p=0.30・売上で ROI/セグメント案とバッチを比べていない

ヒント(段階的開示)

ヒント1 — 方向性
1 通の費用を「入力」「出力」「思考」「作り直し」「時期」に分け、料金表のどの行で数えるか確かめる。効果は「売上」でなく「1 クリックで会社に残るお金」で。2,000 通ずつで +0.5pt は偶然でも出るか。
ヒント2 — アプローチ
  • 出力単価は入力の 5 倍。思考トークンも出力として課金
  • キャッシュには最小トークン数がある。届かせるために例文を足すと安くなることも
  • 文面は前日に決まる → バッチ(50%)
  • 比較相手は「セグメント × 数案を一度だけ作る」
  • +0.5pt を見つけるのに 1 群何通いるか
ヒント3 — 誘導
1通 = (共通 × 入力orキャッシュ単価 + 会員 × 入力単価 + (出力+思考) × 出力単価) ÷ 100万
      × (バッチなら 0.5) × 1/(1 − 差し戻し率)
12か月 = 11・12月は導入価格、1〜10月は倍
1クリックの限界利益 = 3% × (8,000×30% − 500 − 300) = 48円
分岐の上げ幅(pt) = 1通の円 ÷ 48円 × 100

問題点分析(8点)

#問題点分類何が起きているか(出所つき)改善方法
1出力を入力単価で数えた単価出力は $3.75 で入力の 5 倍 <公式>。150 トークンだけで 1 通の費用が 1.9 倍入力・出力を別単価で
2思考トークンを数えていない単価出力単価は思考トークンを含む <公式>。既定の思考量で平均 450 <仮定>思考量を最小に。usage_metadata で実数を取る
3入力は会員部分の 500 だけ量共通 3,000+会員 700=3,700 <仮定>。7.4 倍プロンプト全体をトークン数で測る
4「キャッシュで 90% 引き」が効かない単価共通 3,000 は最小 4,096 に届かない <公式>。設定しても月 $94,091(S3')例文を足して 4,200 にすると逆に安い(S3 月 $41,321)
5導入価格を 12 か月に伸ばした時期3.8 Flash は 2026-12-31 まで。1 月から倍 <公式>。12 か月中 10 か月が倍月ごとにその月の単価で
6作り直しを数えていない量差し戻し 12% → 期待試行 1/(1−0.12)=1.136 回 <導出>検査は LLM の外。幾何分布で数える
7効果が偶然の範囲で、売上で数えた効果2,000 通ずつで p=0.30、95%CI [−0.44, +1.44]pt。1 クリック 売上 240 円 / 限界利益 48 円 <導出>1 群 14,406 通以上。ROI は限界利益で
8比較相手が「何もしない」だけ・即時生成比較セグメント 300×4 案なら月 $55(S5)。文面は前日に決まるのに標準単価バッチとセグメント案を比べ、上積み 0.083pt で判断

費用の積み上がりと分岐の上げ幅(SVG)

① 1通の費用はどこで膨らむか(100万通あたり $、2027年1月の単価)導出: 料金表(公式, 2026-10-09)× トークン数(仮定)後輩の見積もり$488出力を出力単価に+$450 → $938思考トークン+$1,687 → $2,625入力の実際の量+$2,400 → $5,025作り直し ÷0.88+$685 → $5,7101月から倍+$5,710 → $11,420実際(S1)$11,420見積もりの 23.4 倍。いちばん効くのは「思考トークン」と「入力の実際の量」と「導入価格の期限」② 黒字に要るクリック率の上げ幅(pt)と、後輩の A/B の 95% 信頼区間1クリックの限界利益 48円(仮定から導出)。灰色の帯 = A/B の差の95%CI [−0.44, +1.44]、線 = 平均 +0.5ptS1 後輩案の実際3.569ptS3' キャッシュ効かず2.450ptS2 3.8 バッチ・思考最小1.225ptS3 4,200で明示キャッシュ1.076ptS4 Flash-Lite バッチ0.084ptS5 セグメント×4案0.001pt0pt1pt2pt3pt4pt帯の外(右)にある案は、A/B の結果から見てほぼ黒字にならない。S4・S5 だけが帯の中で 0 に近い

パターン: Unit Economics(1 通あたりの費用 vs 1 通あたりの限界利益)+ 損益分岐の上げ幅を A/B の信頼区間と重ねて読む。

Bad → Good(コード差分)

Bad
# Bad: 後輩の見積もり(入力単価だけで全部を数える)
PRICE = 0.75 / 1_000_000          # 入力単価。出力 $3.75 を見ていない
tokens = 500 + 150                # 会員部分の入力だけ。共通 3,000 と思考 450 が無い
monthly = tokens * PRICE * 12_000_000   # 導入価格のまま12か月へ
monthly *= 0.1                    # 「キャッシュで90%引き」← 最小4,096に届かず効かない
roi = 12_000_000 * 0.005 * 240 / monthly  # 売上240円/クリック、A/B は p=0.30

# 結果: 月 $5,850 → 実際は 1月から月 $137,045(23.4倍)
Good
# Good: 料金表の行ごとに数え、作り直しと時期を入れる
p = m.price_on(day)                       # 2027-01-01 から倍になる単価
shared = p.cached if cache_applies(m, w) else p.input  # 4,096 未満は効かない
one = (w.shared_tokens * shared + w.member_tokens * p.input
       + (w.visible_out + w.thinking_out) * p.output) / MTOK  # 思考は出力単価
if w.batch:
    one *= BATCH_FACTOR                   # 前日確定の文面ならバッチ 50%
per_mail = one / (1 - w.reject_rate)      # 作り直しは幾何分布(1+r ではない)

be_pt = per_mail_yen / Funnel().margin_per_click() * 100  # 48円/クリックで割る
# 結果: S1 分岐 3.57pt(P=0%)/S4 Flash-Lite バッチ 0.084pt/S5 セグメント 月$55

適用パターン: Value Object(Price・Workload を frozen dataclass で)+ 有効期間つき単価(price_on)。書籍の章対応は無し(E の日のため)。

模範解答

費用と効果を数えるコード(llm_unit_econ.py)

"""会員ごとの LLM 生成メール文の単位経済(Python 3.12+、標準ライブラリのみ)。

出所ラベル:
  <公式>  Gemini API 料金表・キャッシュ・バッチの文書(参照日 2026-10-09)
          https://ai.google.dev/gemini-api/docs/pricing
          https://ai.google.dev/gemini-api/docs/caching
          https://ai.google.dev/gemini-api/docs/batch-mode
  <仮定>  トークン数・差し戻し率・CVR・客単価・粗利率など社内の数字
  <導出>  上の 2 つから計算した値
"""
from __future__ import annotations

import math
from dataclasses import dataclass, replace
from datetime import date

YEN_PER_USD = 150                 # <仮定>
MTOK = 1_000_000
HOURS_PER_MONTH = 730             # 365 × 24 ÷ 12
BATCH_FACTOR = 0.5                # <公式> バッチは標準の 50%(目標 24 時間以内)


@dataclass(frozen=True)
class Price:
    """100 万トークンあたりのドル単価。output は思考トークンを含む(<公式>)。"""
    input: float
    output: float
    cached: float
    storage_per_hour: float       # 明示キャッシュの保管 $/100万トークン/時


@dataclass(frozen=True)
class Model:
    name: str
    price: Price
    promo_until: date | None = None     # この日までは price、翌日から after
    after: Price | None = None
    min_cache_tokens: int | None = None  # None = 今回は確かめていないので使わない

    def price_on(self, day: date) -> Price:
        """その日に効く単価を返す。導入価格の期限切れを見落とさないための入口。"""
        if self.promo_until and day > self.promo_until:
            assert self.after is not None
            return self.after
        return self.price


# <公式> 参照日 2026-10-09。3.8 Flash は 2026-12-31 までの価格で、2027-01-01 から倍になる
FLASH_38 = Model(
    "gemini-3.8-flash",
    Price(0.75, 3.75, 0.075, 0.50),
    promo_until=date(2026, 12, 31),
    after=Price(1.50, 7.50, 0.15, 1.00),
    min_cache_tokens=4_096,               # <公式> 3.x Flash のキャッシュ最小トークン数
)
FLASH_LITE_25 = Model("gemini-2.5-flash-lite", Price(0.10, 0.40, 0.01, 1.00))


@dataclass(frozen=True)
class Workload:
    """1 通ぶんの生成の形。トークン数は <仮定>(社内プロンプトの実測を入れる場所)。"""
    shared_tokens: int        # 全会員で同じ先頭部分: 指示・景表法ルール・例文
    member_tokens: int        # 会員ごとの属性・購買履歴・キャンペーン情報
    visible_out: int          # 件名+本文の出力
    thinking_out: int         # 思考トークン(出力単価で課金)
    reject_rate: float        # 検査で差し戻して作り直す割合
    batch: bool = False
    explicit_cache: bool = False


def cache_applies(m: Model, w: Workload) -> bool:
    """明示キャッシュが実際に効くか。最小トークン数に届かなければ効かない。"""
    return (w.explicit_cache and m.min_cache_tokens is not None
            and w.shared_tokens >= m.min_cache_tokens)


def cost_per_accepted_usd(m: Model, w: Workload, day: date) -> float:
    """検査を通った 1 通あたりのドル。作り直しの分も含む。"""
    p = m.price_on(day)
    shared_rate = p.cached if cache_applies(m, w) else p.input
    one_call = (w.shared_tokens * shared_rate + w.member_tokens * p.input
                + (w.visible_out + w.thinking_out) * p.output) / MTOK
    if w.batch:
        one_call *= BATCH_FACTOR
    attempts = 1 / (1 - w.reject_rate)   # 幾何分布の期待試行回数。1 + r ではない
    return one_call * attempts


def monthly_cost_usd(m: Model, w: Workload, accepted: int, day: date) -> float:
    """月の費用。明示キャッシュを使うなら保管料(1 本を常時保持)を足す。"""
    usd = cost_per_accepted_usd(m, w, day) * accepted
    if cache_applies(m, w):
        usd += w.shared_tokens / MTOK * m.price_on(day).storage_per_hour * HOURS_PER_MONTH
    return usd


def plan_cost_usd(m: Model, w: Workload, accepted: int, months: list[date]) -> float:
    """計画期間の合計。月ごとにその月の単価で数える。"""
    return sum(monthly_cost_usd(m, w, accepted, d) for d in months)


def bad_estimate_usd(accepted: int) -> float:
    """後輩案: (入力500+出力150) を全部「入力単価 $0.75」で数える。"""
    return (500 + 150) * 0.75 / MTOK * accepted


# ---- 効果側 ----

@dataclass(frozen=True)
class Funnel:
    """<仮定> クリックから注文まで。"""
    cvr_per_click: float = 0.03
    aov_yen: int = 8_000
    gross_margin: float = 0.30
    coupon_yen: int = 500          # 増えた注文でもクーポンは使われる
    fulfillment_yen: int = 300     # 配送・決済手数料など注文ごとの変動費

    def margin_per_order(self) -> float:
        return self.aov_yen * self.gross_margin - self.coupon_yen - self.fulfillment_yen

    def margin_per_click(self) -> float:
        return self.cvr_per_click * self.margin_per_order()

    def revenue_per_click(self) -> float:
        return self.cvr_per_click * self.aov_yen


def breakeven_uplift_pt(cost_per_email_yen: float, f: Funnel) -> float:
    """1 通の費用を回収するのに要るクリック率の上げ幅(ポイント)。"""
    return cost_per_email_yen / f.margin_per_click() * 100


def two_prop_test(c1: int, n1: int, c2: int, n2: int) -> tuple[float, float, tuple[float, float]]:
    """2 群の比率の差: (z, 両側 p 値, 差の 95% 信頼区間[ポイント])。"""
    p1, p2 = c1 / n1, c2 / n2
    pool = (c1 + c2) / (n1 + n2)
    z = (p2 - p1) / math.sqrt(pool * (1 - pool) * (1 / n1 + 1 / n2))
    p = math.erfc(abs(z) / math.sqrt(2))
    se = math.sqrt(p1 * (1 - p1) / n1 + p2 * (1 - p2) / n2)
    d = p2 - p1
    return z, p, ((d - 1.96 * se) * 100, (d + 1.96 * se) * 100)


def n_per_arm(p_base: float, uplift: float, z_a: float = 1.96, z_b: float = 0.8416) -> int:
    """検出力 80%・両側 5% で uplift(比率の差)を見つけるのに要る 1 群の通数。"""
    p2 = p_base + uplift
    var = p_base * (1 - p_base) + p2 * (1 - p2)
    return math.ceil((z_a + z_b) ** 2 * var / uplift ** 2)


def prob_above(mean_pt: float, sd_pt: float, threshold_pt: float) -> float:
    """真の上げ幅が threshold を超える確率(A/B の結果を正規近似で読む)。"""
    return 0.5 * math.erfc((threshold_pt - mean_pt) / (sd_pt * math.sqrt(2)))


def extra_uplift_needed_pt(cost_a_usd: float, cost_b_usd: float, f: Funnel,
                           sends: int | None = None) -> float:
    """A が B より高い分を回収するのに、A が B より上乗せすべきクリック率(ポイント)。"""
    sends = sends or SENDS_PER_MONTH
    return (cost_a_usd - cost_b_usd) * YEN_PER_USD / (sends * f.margin_per_click()) * 100


# ---- 今日の数字 ----

SENDS_PER_MONTH = 3_000_000 * 4              # <仮定> 許諾済み会員 300 万人 × 月 4 通
PLAN = [date(2026, 11, 1)] + [date(2026 + (m > 12), (m - 1) % 12 + 1, 1) for m in range(12, 23)]
# 2026-11 から 12 か月(11・12 月が導入価格、1〜10 月が通常価格)

BAD_ACTUAL = Workload(shared_tokens=3_000, member_tokens=700, visible_out=150,
                      thinking_out=450, reject_rate=0.12)
SCENARIOS: dict[str, tuple[Model, Workload, int]] = {
    "S1 後輩案の実際(3.8 Flash 標準・思考あり)": (FLASH_38, BAD_ACTUAL, SENDS_PER_MONTH),
    "S2 3.8 Flash バッチ・思考最小": (FLASH_38, replace(BAD_ACTUAL, thinking_out=30, batch=True), SENDS_PER_MONTH),
    "S3 3.8 Flash 標準・思考最小・例文を足し 4,200 で明示キャッシュ":
        (FLASH_38, replace(BAD_ACTUAL, shared_tokens=4_200, thinking_out=30, explicit_cache=True), SENDS_PER_MONTH),
    "S3' 同じく 3,000 のまま明示キャッシュ(効かない)":
        (FLASH_38, replace(BAD_ACTUAL, thinking_out=30, explicit_cache=True), SENDS_PER_MONTH),
    "S4 2.5 Flash-Lite バッチ・思考なし・差し戻し 20%":
        (FLASH_LITE_25, replace(BAD_ACTUAL, thinking_out=0, batch=True, reject_rate=0.20), SENDS_PER_MONTH),
    "S5 セグメント 300 × 4 案 × 月 4 回(3.8 Flash 標準)": (FLASH_38, BAD_ACTUAL, 300 * 4 * 4),
}

AB = dict(c1=42, n1=2_000, c2=52, n2=2_000)   # 後輩の A/B(2.1% → 2.6%)


def report() -> None:
    f = Funnel()
    jan = date(2027, 1, 1)
    bad_m = bad_estimate_usd(SENDS_PER_MONTH)
    print(f"後輩の見積もり       月 ${bad_m:,.0f}  年 {bad_m * 12 * YEN_PER_USD / 1e4:,.0f} 万円")
    z, p, ci = two_prop_test(**AB)
    sd = (ci[1] - ci[0]) / (2 * 1.96)
    mean = (ci[0] + ci[1]) / 2
    print(f"A/B 2,000×2          z={z:.2f}  p={p:.2f}  差の95%CI [{ci[0]:+.2f}, {ci[1]:+.2f}] pt")
    print(f"+0.5pt を見つける通数 1群 {n_per_arm(0.021, 0.005):,}")
    print(f"1クリックの価値      売上 {f.revenue_per_click():.0f} 円 / 限界利益 {f.margin_per_click():.0f} 円")
    rev_gain = SENDS_PER_MONTH * 0.005 * f.revenue_per_click()
    mar_gain = SENDS_PER_MONTH * 0.005 * f.margin_per_click()
    print(f"+0.5pt の月効果      売上 {rev_gain / 1e4:,.0f} 万円 / 限界利益 {mar_gain / 1e4:,.0f} 万円")
    print(f"後輩の ROI           {rev_gain * 12 / (bad_m * 12 * YEN_PER_USD):.1f} 倍(売上 ÷ 見積もり)")
    print()
    print(f"{'シナリオ':44s} {'1通(円)':>8s} {'月(11月)':>10s} {'月(1月〜)':>10s} {'12か月(万円)':>11s} {'分岐pt':>7s} {'P(黒字)':>7s}")
    for name, (m, w, n) in SCENARIOS.items():
        per_yen = cost_per_accepted_usd(m, w, jan) * YEN_PER_USD
        nov = monthly_cost_usd(m, w, n, PLAN[0])
        after = monthly_cost_usd(m, w, n, jan)
        total = plan_cost_usd(m, w, n, PLAN) * YEN_PER_USD / 1e4
        # 分岐とP(黒字)は 1 対 1 の全会員配信のときだけ意味がある(S5 は通数ではなく 1 回の生成費)
        be = breakeven_uplift_pt(after * YEN_PER_USD / SENDS_PER_MONTH, f)
        pr = "—" if n != SENDS_PER_MONTH else f"{prob_above(mean, sd, be):.1%}"
        print(f"{name:44s} {per_yen:8.3f} {nov:10,.0f} {after:10,.0f} {total:11,.0f} {be:7.3f} {pr:>7s}")
    lite = monthly_cost_usd(*SCENARIOS["S4 2.5 Flash-Lite バッチ・思考なし・差し戻し 20%"], jan)
    seg = monthly_cost_usd(*SCENARIOS["S5 セグメント 300 × 4 案 × 月 4 回(3.8 Flash 標準)"], jan)
    print()
    print(f"1対1(S4) がセグメント(S5) に勝つのに要る上積み {extra_uplift_needed_pt(lite, seg, f):.3f} pt")
    print(f"S4 を u=+0.5pt で回したときの月の限界利益 {(mar_gain - lite * YEN_PER_USD) / 1e4:,.0f} 万円")


if __name__ == "__main__":
    report()

変異テスト(本体を 1 か所ずつ壊して 15 テストを流す)

赤  M1 出力も入力単価で数える(後輩案)  test_output_token_uses_output_rate, test_thinking_tokens_are_billed_as_output
赤  M2 思考トークンを数えない  test_thinking_tokens_are_billed_as_output
赤  M3 キャッシュの最小トークン数を見ない  test_cache_below_minimum_does_nothing
赤  M4 導入価格が年中続く  test_plan_has_2_promo_months_and_10_full, test_promo_ends_on_dec_31
赤  M5 作り直しを 1 + r で数える  test_retry_is_geometric_not_linear
赤  M6 キャッシュ保管料を足さない  test_cache_at_minimum_uses_cached_rate_plus_storage
赤  M7 1 クリックの価値を売上で数える  test_breakeven, test_click_value_is_margin_not_revenue
赤  M8 クーポン原価を引かない  test_breakeven, test_click_value_is_margin_not_revenue
赤  M9 バッチ割引を忘れる  test_batch_is_half
赤  M10 片側検定にする(p を半分)  test_ab_2000_is_not_significant

10 種すべて 1 回目で赤。ただし変異は自分が思いついた誤りだけなので、為替や月の日数の誤りは測れていない。サンプルサイズの式は教科書の例(10%→12% で 1 群約 3,839)と照合した。

実行例

後輩の見積もり       月 $5,850  年 1,053 万円
A/B 2,000×2          z=1.04  p=0.30  差の95%CI [-0.44, +1.44] pt
+0.5pt を見つける通数 1群 14,406
1クリックの価値      売上 240 円 / 限界利益 48 円
+0.5pt の月効果      売上 1,440 万円 / 限界利益 288 万円
後輩の ROI           16.4 倍(売上 ÷ 見積もり)

シナリオ                                            1通(円)     月(11月)     月(1月〜)    12か月(万円)    分岐pt   P(黒字)
S1 後輩案の実際(3.8 Flash 標準・思考あり)                    1.713     68,523    137,045      22,613   3.569    0.0%
S2 3.8 Flash バッチ・思考最小                           0.588     23,523     47,045       7,762   1.225    6.5%
S3 3.8 Flash 標準・思考最小・例文を足し 4,200 で明示キャッシュ       0.516     20,661     41,321       6,818   1.076   11.5%
S3' 同じく 3,000 のまま明示キャッシュ(効かない)                  1.176     47,045     94,091      15,525   2.450    0.0%
S4 2.5 Flash-Lite バッチ・思考なし・差し戻し 20%             0.040      3,225      3,225         580   0.084   80.7%
S5 セグメント 300 × 4 案 × 月 4 回(3.8 Flash 標準)        1.713         27         55           9   0.001       —

1対1(S4) がセグメント(S5) に勝つのに要る上積み 0.083 pt
S4 を u=+0.5pt で回したときの月の限界利益 240 万円
  • S1 の 12 か月は 2 億 2,613 万円で見積もり 1,053 万円の 21.5 倍。1 月以降は月約 2,056 万円で、+0.5pt が本当でも限界利益は月 288 万円 → 月 1,768 万円の赤字
  • P(黒字) は A/B の結果を正規分布で読んだ値。A/B 自体が弱いので粗い。S5 は別物なので出していない

推奨案と稟議の書き直し

件名: メール文の生成は「セグメント × 数案」から始め、1 対 1 は効果を測ってから Flash-Lite のバッチで判断する

結論: 後輩案は 12 か月 2.26 億円で、効果が本当でも月 1,768 万円の赤字。まずセグメント 300 × 4 案(月 $55)。

効果の測り方: 11 月の 2 回の配信で 1 群 14,406 通以上 × 3 群(現行/セグメント案/1 対 1 の Flash-Lite 案)。増分利益は注文 1 件 1,600 円で数える。

1 対 1 に進む条件: Flash-Lite バッチ(月 $3,225 ≈ 48 万円)がセグメント案より +0.083pt 以上。3.8 Flash はキャッシュ込みでも分岐 1.08pt で採らない。

景表法: 割引率・期限・対象商品はマスタから差し込み、LLM に書かせない。禁止語とマスタに無い数字を機械で検査。配信前に 50 通を人が抜き取り。

費用の見張り: usage_metadata を日次で BigQuery へ、1 通あたりの円を出す。生成費が月 60 万円を超えたら通知。

見直す条件: 3.8 Flash の単価が下がる/1 対 1 の効果が +1.1pt を超えて測れた/配信が行動起点の即時に変わった。

迷った案と採らなかった理由

案12 か月判断前提が変わったら
S1 後輩案(3.8 Flash 標準・思考あり)2億2,613万円却下。分岐 3.57pt—
S2 3.8 Flash バッチ・思考最小7,762万円却下。分岐 1.23pt、P(黒字) 6.5%大きな A/B で +1.2pt 以上が出たら
S3 3.8 Flash+例文を足して明示キャッシュ6,818万円却下。ただし長いプロンプトの方が安い(1通 1.176→0.516円)別案件で 3.8 Flash を使うとき。バッチとの併用は要確認
S4 2.5 Flash-Lite バッチ・思考なし580万円候補。分岐 0.084pt。質は仮定、A/B で確かめる差し戻し 50% 超・禁止語の検出漏れで止める
S5 セグメント × 数案9万円先に採用1 対 1 が +0.083pt 以上なら S4 へ
開封者(40%)だけ 1 対 1S4 の約 4 割保留。効果の差は未測定開封者でだけ効果が大きいと分かったら
自社 GPU で小型モデル未試算扱わない。運用の人件費が勝つ見込みが薄い生成量が 10 倍以上になったら

ポイント解説

  1. LLM の費用は出力と思考で決まることが多い: 入力だけで見積もると桁が変わる(今回 23.4 倍)。
  2. キャッシュと導入価格は注記で決まる: 4,096 未満のキャッシュは効かない。例文を足すと安くなることも。導入価格の期限は 12 か月中 10 か月を倍にする。
  3. 効果は限界利益と信頼区間: 240 円でなく 48 円。2,000 通の +0.5pt は 0 を含む。
  4. 比較相手をセグメント案に: 1 対 1 はその差を回収する上積み 0.083pt で判断。

実務への応用

商品説明文・プッシュ通知・問い合わせの一次回答でも、まず「1 件の円」と「1 件で残るお金」を並べる。usage_metadata を BigQuery に入れ、単価は dbt のシードで改定日つきで持つ。数字はマスタから差し込み、文の自由度と景表法のリスクを分ける。

扱わなかった範囲(止め方)

  • トークン数・思考・差し戻し・CVR・粗利率はすべて仮定。count_tokens と本番の usage_metadata で測る。思考量の設定と最小値はモデルごとに要確認
  • 単価は Gemini API(Google AI)の料金表。Vertex AI 経由の単価・Provisioned Throughput は確かめていない
  • 配信停止率・ブランド・到達率への影響は数えていない
  • 景表法は第 5 条第 2 号(有利誤認表示)を念頭に置いたが、消費者庁のページは取得できず(HTTP 403)原文未確認。法務の確認が要る
  • 論点を 11 個挙げ 9 個を数えた(レイテンシ要件・レート制限は扱わず)。11 個は自分で挙げたもので、外部の枠組みとは照合していない

発展問題: カゴ落ちから 1 時間後の 1 対 1 メールではバッチが使えない。標準単価で、回収率がいくら上がれば Flash-Lite と 3.8 Flash それぞれで黒字になるか。

今日のまとめ

LLM 生成の稟議は「入力トークン × 入力単価」では桁がずれる。出力・思考・キャッシュの最小値・導入価格・作り直しを料金表の注記まで数え、効果は限界利益と信頼区間で測り、比較相手にセグメント × 数案を置く。

自己評価(あとで記入)