夕方に依頼を投げた直後、返りが止まりました。ステータスラインの右端に出ている残量の表示が色づいていて、ベースラインのクォータが尽きる境目に立っていることが分かりました。そこで手が止まったのは 2 分ほどでしたが、そのあいだに考えたのは「このまま続けてよいのか」という一点でした。
続けられるかどうかは、そのときの気分ではなく、設定ひとつで決まっておりました。私はそれを、残量が色づいてから知ったのです。
待つか払うかは、設定ひとつで入れ替わります
公式の Plans ページによれば、すべてのプランにベースラインのクォータがあり、Google AI Pro と Ultra では 5 時間ごとに回復して、週の上限に当たるまで続きます。Pro と Ultra 以外は週ごとの回復です。同じページには、レート制限がエージェントの作業量と相関し、プロンプトごとに異なるとも書かれております。つまり「あと何回頼めるか」を回数で数えることはできません。
ベースラインを超えたあとの扱いは、購入済みの AI クレジットで続けるかどうかという別の話です。これを決めているのが「AI Credit Overages」の設定で、値は Never と Always の二つだけです。Never なら回復まで待ち、Always なら自動でクレジットを使い、回復が来たら自動でベースラインへ戻ります。CLI 側では ~/.gemini/antigravity-cli/settings.json の useG1Credits、あるいは /config と /settings のパネルにある Use G1 Credits が同じ役割を担います。
手元で残量を確かめる場所は三つあります。ステータスラインの右端の表示、/credits で開く残高と消費の内訳、そして /usage(別名 /quota)で開くモデル別の残りです。プランごとの損益の考え方はAntigravity の料金と使用量 — 無料枠・Pro・AI Ultra の損益分岐に書いておりますので、金額の側から決めたい方はそちらも合わせてご覧ください。
Always のままにしていた週に、私が落としたもの
私は個人開発でアプリをいくつか運用しており、ストア文面の下書きづくりや Lab 側のサイト保守を、人が寝ている時間帯にまとめて回しております。設定は Always のままでした。いま思えば、止まらないほうが安心だと感じていたのは、私が残量を読めていなかったせいかもしれません。
その週、夜間の枠が上限をまたぎました。朝に残高を見ると、確かに減っておりました。こたえたのは金額そのものではなく、何に使われたのかを思い出せないことでした。急いでいた作業でもなく、翌日でよかった下書きに、待てば無料で通る分のお金を払っていたわけです。
そこから引いた線は一つだけです。人が待っている作業にはお金を、誰も待っていない作業には時間を。 この向きを決めてからは、設定を触る回数そのものが減りました。
三つの箱に分けてから、設定を決めます
作業を細かく分類しようとして、かえって決められなくなった時期もありました。いまは三つの箱に放り込むだけにしております。
| 作業の箱 | 払うもの | 上限に当たったときの向き | 間違えたときの出方 |
|---|---|---|---|
| 人が待っている(受託の締切前・稼働中の不具合) | お金 | Always で続ける | 想定より残高が減ります |
| 誰も待っていない(夜間のまとめ処理・下書き生成) | 時間 | Never で回復を待つ | 朝に未完了のまま残ります |
| 試し打ち・学習のための往復 | どちらも払わない | Never のまま軽いモデルへ寄せる | 気づかないうちに週の上限へ近づきます |
三つめの箱がいちばん見落としやすい場所でした。試し打ちは 1 回が軽いぶん回数が増え、週の上限に当たる原因になっていたのです。Never にしておけば、上限が近づいたときに手が止まってくれます。止まることを不便と受け取るか、知らせと受け取るかで、この設定の評価は反対になります。私はいまは、知らせのほうだと感じています。
無人の枠には、走る前の一拍を置いています
夜間の枠は、私が見ていません。残量が色づいても気づける人がいないので、設定を Never にするだけでは足りず、走る前に残りを確かめる一拍が要ります。
残量を機械から読める場所として使えるのが、ステータスラインのスクリプトです。エージェントの状態が変わるたびに CLI がスクリプトを呼び、状態の JSON を標準入力へ流します。その JSON の quota に、バケットごとの remaining_fraction・reset_time・reset_in_seconds が入っています。これを最新の一枚だけ書き置きます。
#!/usr/bin/env bash
# ~/.gemini/antigravity-cli/quota-snapshot.sh
# 画面に1行返しつつ、いまのクォータ残量を1枚だけ書き置きます(履歴は貯めません)
set -u
SNAP="${HOME}/.gemini/antigravity-cli/quota_snapshot.json"
PAYLOAD="$(cat)" # 状態が変わるたびに JSON が標準入力へ届きます
printf '%s' "${PAYLOAD}" \
| jq -c --arg at "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
'{captured_at: $at, tier: (.plan_tier // "unknown"), quota: (.quota // {})}' \
> "${SNAP}.tmp" 2>/dev/null \
&& mv "${SNAP}.tmp" "${SNAP}"
printf '%s' "${PAYLOAD}" | jq -r '
(.quota // {}) | to_entries
| if length == 0 then "quota -"
else map("\(.key) \(((.value.remaining_fraction // 0) * 100) | floor)%") | join(" ")
end'設定は statusLine のブロックで結び、stack_with_default を立てて既定の行を残します。書き置きを既定の表示と入れ替えてしまうと、普段の作業が見えにくくなるからです。
{
"statusLine": {
"type": "command",
"command": "~/.gemini/antigravity-cli/quota-snapshot.sh",
"stack_with_default": true
},
"useG1Credits": false
}一時ファイルへ書いてから mv で置き換えているのは、このスクリプトが状態の変化ごとに何度も走るためです。直接書き込むと、読み出した側が途中の壊れた JSON を拾います。消費を台帳として貯めていく話はステータスラインでコストを追えるようにするまでに、3か所でつまずきましたに分けて書いておりますので、貯めるほうに関心のある方はそちらへどうぞ。ここでは貯めずに、最新の一枚だけを見ます。
走る前の一拍は、この一枚を読んで「いま走らせるか、回復を待つか」を返すだけの小さなゲートです。
#!/usr/bin/env python3
"""無人で走らせる枠の前に一拍置くゲート。
exit 0 … いま走らせて差し支えありません
exit 75 … 後回し(呼び出し側で見送ります)
残りが読めないときは「後回し」に倒します。
"""
import json
import os
import sys
import time
SNAP = os.path.expanduser("~/.gemini/antigravity-cli/quota_snapshot.json")
MIN_REMAINING = 0.25 # ベースラインの残りがこれを下回ったら無人実行は見送ります
MAX_AGE_SEC = 6 * 3600 # 書き置きの賞味期限(CLI を開いていない間は更新されません)
DEFER = 75
def load_snapshot(path):
try:
age = time.time() - os.path.getmtime(path)
with open(path, encoding="utf-8") as fp:
return json.load(fp), age
except (OSError, ValueError):
return None, None
def main():
snap, age = load_snapshot(SNAP)
if snap is None:
print("defer: 書き置きを読めませんでした")
return DEFER
if age > MAX_AGE_SEC:
print(f"defer: 書き置きが古すぎます({age / 3600:.1f} 時間前)")
return DEFER
buckets = snap.get("quota") or {}
if not buckets:
print("defer: quota が空でした(初回の呼び出し前かもしれません)")
return DEFER
name, worst = min(buckets.items(), key=lambda kv: kv[1].get("remaining_fraction", 0.0))
remaining = worst.get("remaining_fraction", 0.0)
wait_min = int(worst.get("reset_in_seconds", 0) // 60)
if remaining < MIN_REMAINING:
print(f"defer: {name} の残りが {remaining:.0%} です(回復まで約 {wait_min} 分)")
return DEFER
print(f"go: {name} の残りは {remaining:.0%} です")
return 0
if __name__ == "__main__":
sys.exit(main())手元では次のように出ます。
$ python3 ~/bin/quota_preflight.py
go: gemini-weekly の残りは 93% です
$ python3 ~/bin/quota_preflight.py
defer: gemini-weekly の残りが 11% です(回復まで約 74 分)呼び出し側は、見送りを異常として扱わないほうが静かに回ります。
# 夜間の枠から呼びます
python3 ~/bin/quota_preflight.py || exit 0
agy -p "$(cat ~/prompts/nightly_draft.md)"最も残りが少ないバケットで判定しているのは、モデルごとに別々の枠があり、どれか一つでも尽きればその作業が止まるからです。平均で見ると、止まる側の枠が薄まって見えなくなります。
残りが読めないときは、走らせないほうを選びます
この仕組みには、はっきりした弱点が一つあります。それに気づいたのは、朝のログに go の行だけが並んでいるのを見返したときでした。書き置きは CLI が起きている間しか更新されません。前の晩から一度も CLI を開いていなければ、ゲートは古い残量で「行ってよし」と答えてしまいます。
古い値で判断するゲートは、ゲートが無い状態より危ないのです。だから賞味期限を切って、分からないときは後回しに倒しています。quota が空のときも同じ扱いにしました。最初の呼び出し前はこのフィールドが入っていないことがあり、空を「残り 0」と読むか「不明」と読むかで挙動が変わってしまうためです。
もう一つ、混ぜて考えると迷う点があります。Never に寄せても、週の上限そのものは動きません。この設定が決めているのは「上限をお金で越えるかどうか」だけです。週の上限に当たる回数そのものを減らしたいなら、頼み方と渡す範囲の側を直す話になります。
次にやること
まず /usage を開いて、いまのモデル別の残りと回復までの時間を眺めてみてください。そのうえで Overages を Never にして、一週間だけ過ごしてみるのがおすすめです。止まった場面を数えれば、自分の作業がどの箱に入るのかが自然に分かってきます。
私自身、無人の枠のしきい値は先週も一度上げ直しました。まだ調整の途中ですが、同じところで迷っている方の手がかりになれば嬉しいです。お読みいただきありがとうございました。