AIのトークン・クォータ (利用枠) 残量をグラフ化する 〜AntigravityとZabbixの場合〜

クォータ残量の推移を知りたい
普段利用しているAIの、トークン・クォータ (利用枠) 残量が今どれくらいなのかを確認する行為は、趣味や仕事のペース配分を決めるために不可欠となった。欲を言えばこの残量確認は、ある瞬間の値ではなくて時系列での変化、つまりグラフの形で可視化して行いたい。なぜなら、“このままAIをぶん回していたらあと何時間でLimitに達する”、という先の見通しを立てたいからだ。
そのために、何らかの仕掛けを作ることにしよう。時系列データを取得してグラフ化することができたならば、次はその情報をスマホのホーム画面や通知領域に表示したり、電子ペーパー端末やスマートウォッチに表示するという応用もできるはず。そこで本記事では、最初の一歩として、自分が現在使っているプラン「Google AI Ultra 20x」のトークン・クォータ残量をZabbixに集約して記録に残し、推移をグラフ化してみることにした。
グラフ化する仕掛け
Step-1. agyコマンド「/usage」を使う
Antigravity CLIのコマンドagyに、次のような引数-p '/usage' --output-format jsonを付けて実行すると、「Weekly Limit Remaining」や「Five Hour Limit Remaining」という項目で、トークン・クォータ残量の数値などが得られる。
$ agy -p '/usage' --output-format json > ./agy_20261107-2001.json
実際のJSON出力は次の通りだ。わかりやすい項目分けがされている。
{
"conversation_id": "",
"status": "SUCCESS",
"response": "Gemini Models\tWeekly Limit Remaining\t96%\t2026-11-14T02:40:30Z\nGemini Models\tFive Hour Limit Remaining\t95%\t2026-11-07T15:00:09Z\nClaude and GPT models\tWeekly Limit Remaining\t100%\t2026-11-14T11:01:57Z\nClaude and GPT models\tFive Hour Limit Remaining\t100%\t2026-11-07T16:01:57Z\n",
"duration_seconds": 0,
"num_turns": 0,
"usage": {
"input_tokens": 0,
"output_tokens": 0,
"thinking_tokens": 0,
"cache_read_tokens": 0,
"total_tokens": 0
},
"command": {
"name": "usage",
"data": {
"description": "Within each group, models share a weekly limit and a 5-hour limit. Quota is consumed proportionally to the cost of the tokens. Thus, limits will last longer with shorter tasks or using more cost-effective models. The 5-hour limit smooths out aggregate demand to fairly distribute global capacity across all users, while your weekly limit is tied directly to your individual tier.",
"groups": [
{
"name": "Gemini Models",
"description": "Models within this group: Gemini Flash, Gemini Pro",
"buckets": [
{
"id": "gemini-weekly",
"name": "Weekly Limit Remaining",
"description": "You have used some of your weekly limit, it will fully refresh in 6 days, 15 hours.",
"window": "weekly",
"remaining_fraction": 0.9619837999343872,
"reset_time": "2026-11-14T02:40:30Z"
},
{
"id": "gemini-5h",
"name": "Five Hour Limit Remaining",
"description": "You have used some of your 5-hour limit, it will fully refresh in 3 hours, 58 minutes.",
"window": "5h",
"remaining_fraction": 0.954495906829834,
"reset_time": "2026-11-07T15:00:09Z"
}
]
},
{
"name": "Claude and GPT models",
"description": "Models within this group: Claude Opus, Claude Sonnet, GPT-OSS",
"buckets": [
{
"id": "3p-weekly",
"name": "Weekly Limit Remaining",
"window": "weekly",
"remaining_fraction": 1,
"reset_time": "2026-11-14T11:01:57Z"
},
{
"id": "3p-5h",
"name": "Five Hour Limit Remaining",
"window": "5h",
"remaining_fraction": 1,
"reset_time": "2026-11-07T16:01:57Z"
}
]
}
]
}
}
}Step-2. データ送信
Step-2-1. jqコマンドのお試し
Step-1のJSON出力から欲しい値のみを抽出するjqコマンドを試してみると、たとえば次のようになる。
$ cat ./agy_20261107-2001.json | \ jq -r '(.command.data.groups // [])[] | (.buckets // [])[] | "[\(.id)] \(.remaining_fraction)"' [gemini-weekly] 0.9619837999343872 [gemini-5h] 0.954495906829834 [3p-weekly] 1 [3p-5h] 1
このようなコマンドを、Antigravity CLIを常駐 (agy remote-control serve) させて動かしているAIサーバ上で適切なタイミングにて実行し、値をZabbixサーバに送り込めば、値の可視化ができそうだと予想できる。
今回は環境構築で手抜きをして、「AIサーバからZabbixサーバへ公開鍵認証でのSSHログインだけは可能で、他は何もinstallしない」という条件で、仕掛け作りを進める。
Step-2-2. クォータ残量をStopフックで送信する
AIサーバに、~/.gemini/antigravity-cli/hook_export_quota.shとして次のbashスクリプトを保存する。Zabbixサーバに送り込むためのバイナリデータを、内部のPythonスクリプトで準備するという変態的な作り。動いたから良し。
#!/usr/bin/env bash
set -euo pipefail
# hook_export_quota.sh (Ver.20261107)
# 設定変数 (環境変数による上書き可)
readonly AGY_BIN="${AGY_BIN:-$(command -v agy 2>/dev/null || printf '%s/.local/bin/agy' "$HOME")}"
readonly OUTPUT_JSON="${OUTPUT_JSON:-/tmp/gemini_quota_hook.json}"
readonly LOCK_FILE="${LOCK_FILE:-/tmp/gemini_quota_hook.lock}"
readonly ZABBIX_SSH_TARGET="${ZABBIX_SSH_TARGET:-}"
readonly ZABBIX_HOST="${ZABBIX_HOST:-$(hostname -s 2>/dev/null || uname -n)}"
# 1. フックの入力を読み捨て、エージェントの停止を即座に許可する
if [[ ! -t 0 ]]; then
cat > /dev/null || true
fi
printf '{"decision": "allow"}\n'
# 親子プロセスでの無限ループ防止ガード
if [[ "${GEMINI_QUOTA_HOOK_RUNNING:-0}" == "1" ]]; then
exit 0
fi
# 実行コマンドの存在・実行権限チェック
if [[ ! -x "$AGY_BIN" ]]; then
exit 0
fi
# 2. バックグラウンドでクォータJSONを取得して保存、SSH経由でZabbixへ送信する
(
export GEMINI_QUOTA_HOOK_RUNNING=1
# 別プロセス間での多重起動防止ロック
exec 9>"$LOCK_FILE"
if command -v flock >/dev/null 2>&1; then
flock -n 9 || exit 0
fi
output_dir="$(dirname "$OUTPUT_JSON")"
mkdir -p "$output_dir"
tmp_file="$(mktemp "${OUTPUT_JSON}.tmp.XXXXXX")"
trap 'rm -f "$tmp_file"' EXIT
if "$AGY_BIN" -p "/usage" --output-format json < /dev/null 9>&- > "$tmp_file" 2>/dev/null && [[ -s "$tmp_file" ]]; then
chmod 0644 "$tmp_file"
mv -f "$tmp_file" "$OUTPUT_JSON"
# Zabbixサーバーへ各バケットの残量(%)を送信
if [[ -n "$ZABBIX_SSH_TARGET" ]] && command -v jq >/dev/null 2>&1; then
jq -c --arg host "$ZABBIX_HOST" '{
request: "sender data",
data: [
select(.status == "SUCCESS")
| (.command.data.groups // [])[]
| (.buckets // [])[]
| {
host: $host,
key: "gemini.quota.remaining[\(.id)]",
value: (((.remaining_fraction // 0) * 100) | tostring)
}
]
}' "$OUTPUT_JSON" | ssh -o BatchMode=yes -o ConnectTimeout=5 "$ZABBIX_SSH_TARGET" 'python3 -c "
import sys, socket, struct
payload = sys.stdin.read().encode()
if payload:
packet = b\"ZBXD\x01\" + struct.pack(\"<Q\", len(payload)) + payload
with socket.create_connection((\"127.0.0.1\", 10051), timeout=5) as s:
s.sendall(packet)
s.recv(1024)
"' >/dev/null 2>&1 || true
fi
fi
) </dev/null >/dev/null 2>&1 &
exit 0そして同じく、AIサーバに、~/.gemini/config/hooks.jsonとして次の内容を保存する (既に存在すれば追記する)。ZABBIX_SSH_TARGETに代入しているzabbix.example.comは、公開鍵認証でSSHログイン可能な、各々のZabbixサーバ名に置き換える。
{
"quota-exporter": {
"Stop": [
{
"type": "command",
"command": "ZABBIX_SSH_TARGET=zabbix.example.com \"$HOME/.gemini/antigravity-cli/hook_export_quota.sh\"",
"timeout": 15
}
]
}
}Step-2-3. クォータ残量を定期送信する
AIのエージェントを操作していない時間帯も、クォータ残量を15分間隔でZabbixサーバへ送信するように、AIサーバの次のcrontabを設定する。この仕掛けにより、エージェント操作によらない、一定時間経過後の回復などの残量変化が、15分以内に捉えられる。
*/15 * * * * ZABBIX_SSH_TARGET=zabbix.example.com /home/USER/.gemini/antigravity-cli/hook_export_quota.sh < /dev/null >/dev/null 2>&1Step-3. Zabbix側のItem/Graph設定
Zabbixサーバのウェブ管理画面にて、AIサーバのホスト名 (hostname -s) のホストを作成し、下記のアイテムを、Type = Zabbix trapper, Type of information = Numeric (float), Units = % として登録する。
| Name | Key | 対象モデル | |
|---|---|---|---|
| Gemini Quota Remaining (5h) | gemini.quota.remaining[gemini-5h] | Gemini | |
| Gemini Quota Remaining (Weekly) | gemini.quota.remaining[gemini-weekly] | Gemini | |
| 3P Quota Remaining (5h) | gemini.quota.remaining[3p-5h] | Claude, GPT | |
| 3P Quota Remaining (Weekly) | gemini.quota.remaining[3p-weekly] | Claude, GPT |
あわせて、上記のアイテムで必要なもの (私の場合はGemini関係の2つにした) に対応するグラフを作成すると、クォータ残量の推移をZabbix上のグラフで把握できるようになる。
個人的まとめ
私は個人としてはGemini/Antigravityを利用していて、本格利用を始めた当初の契約プランは「Google AI Ultra 5x」だった。しかし、たとえば複数のエージェントに重めのタスクを投げて働かせたら、クォータの5時間リミットに達することは珍しくないし、1週間のリミットにも達して、2日ぐらい何もできないという事態にも至っていた。
AIのクォータ切れは、たとえばMonokuroCamをがりがり改良したい時などには大変つらい。そこで2026年10月末に、プランを「Google AI Ultra 20x」へ切り替えてみたのだ。下記のグラフの傾きを見ながら振り返ると、余裕が20xの四分の一しかない5xの場合、クォータ切れに達するのはまったく不思議ではない、自分がAIの使い方を変えない限りは、と納得させられた。



