◉ANTIGRAVITY LABEN
記事一覧/Antigravity 基本
◉ Antigravity 基本/2026-09-26初級

Antigravity 2.17.0 のルール枠 20,000 トークンに、手元の運用ルール集を当てて数えました

2.17.0 でルールに専用の 20,000 トークン枠ができました。手元の運用ルール集 6 本を数えると 28,613 トークンで、大きい 2 本がポインタに落ちます。落とされる前に自分で降ろす順番を決める手順です。

Antigravity 2.015rules2AGENTS.md14コンテキストトークン予算model_decision

2.17.0 のチェンジログを読んでいた夜、「Rules no longer crowd out everything else」という一行で手が止まりました。ルールがほかのものを押し出さなくなった、という文は裏を返せば、これまでは押し出していたということなのです。

私自身、Lab 4 サイトの運用ルール集を何度か分割して薄くしてきましたが、それでも合計は膨らんでおりました。あの分厚いルール集は、私が気づかないところでスキルや MCP ツールを追い出していたのかもしれません——そう思うと胃のあたりが重くなりました。

そこで、更新を入れる前に手元のルール集を数えてみることにしました。数えてみると、枠を 8,613 トークン越えておりました。

2.17.0 で決まった二つの上限

先に、ルールのドキュメントに書かれている上限を整理しておきます。上限は二つあり、単位が違います。

上限数え方越えたときの扱い
1 ファイル 24,000 バイト@[label](path) の取り込みを展開したあとのバイト数末尾が切り詰められます
合計 20,000 トークングローバルと always_on のルール全体大きいファイルから順に、パスと説明だけのポインタに降ろされます

この 20,000 トークンは、スキル・サブエージェント・MCP ツールが使う customization の予算とは別枠です。2.17.0 より前は同じ枠を取り合っていたので、ルールが太るとほかが削られていました。

もう一つ大事なのは、model_decision のルールは最初からポインタとして入る、という点です。パスと description だけが前置きに載り、エージェントが必要と判断したときに本文を開きます。つまり枠を越えて降ろされるのも、自分で model_decision にしておくのも、エージェントから見た形はほぼ同じなのです。

手元のルール集を数える小さな道具

数えたかったのは三つです。各ファイルが展開後に 24,000 バイトを越えていないか、always_on の合計が 20,000 トークンに収まっているか、そして収まらないとき、どのファイルからポインタに落ちるのか。

Antigravity 自体は結果を教えてくれますが、それは更新して開いてからの話です。私は入れる前に知りたかったので、ワークスペースのルール一式を読んで試算する Python を書きました。

#!/usr/bin/env python3
"""Antigravity のルール予算を、手元のルール集で事前に測る小さな道具。
- 24,000 バイト/ファイル(@[label](path) 展開後)を超えるものを警告
- always_on(と frontmatter の無い AGENTS.md / GEMINI.md)の合計を 20,000 トークンと比べる
- 超過時は大きいファイルから順にポインタ化される前提で、どれが残るかを試算
トークンは tiktoken cl100k_base の近似値。Gemini の実カウントとは前後します。
"""
import re, sys, pathlib
import tiktoken
 
FILE_LIMIT_BYTES = 24_000
RULES_BUDGET_TOKENS = 20_000
VALID_TRIGGERS = {"always_on", "model_decision", "glob", "manual"}
enc = tiktoken.get_encoding("cl100k_base")
INCLUDE = re.compile(r"@\[[^\]]*\]\(([^)]+)\)")
 
def expand_includes(text: str, base: pathlib.Path) -> str:
    def repl(m):
        target = pathlib.Path(m.group(1)).expanduser()
        if not target.is_absolute():
            target = (base / target).resolve()
        try:
            body = target.read_text(encoding="utf-8")
        except OSError:
            return m.group(0)  # 見つからないときは原文のまま
        return re.sub(r"\A---\n.*?\n---\n", "", body, flags=re.S)  # frontmatter を落とす
    return INCLUDE.sub(repl, text)
 
def read_rule(path: pathlib.Path):
    raw = path.read_text(encoding="utf-8")
    trigger, desc = None, ""
    m = re.match(r"\A---\n(.*?)\n---\n", raw, flags=re.S)
    if m:
        fm = m.group(1)
        t = re.search(r"^trigger:\s*(\S+)", fm, flags=re.M)
        d = re.search(r'^description:\s*"?(.*?)"?\s*$', fm, flags=re.M)
        trigger = t.group(1) if t else "INVALID"
        if trigger not in VALID_TRIGGERS:
            trigger = "INVALID"  # alwaysOn / modelDecision などは黙って捨てられる
        desc = d.group(1) if d else ""
    elif path.name in ("AGENTS.md", "GEMINI.md"):
        trigger = "always_on"  # frontmatter なしで常時有効
    else:
        trigger = "INVALID"  # rules/ 配下で frontmatter 無し → 黙って捨てられる
    body = expand_includes(raw, path.parent)
    return {"path": path, "trigger": trigger, "desc": desc,
            "bytes": len(body.encode("utf-8")), "tokens": len(enc.encode(body))}
 
def main(root: str):
    root = pathlib.Path(root)
    files = [p for p in (root / "AGENTS.md", root / "GEMINI.md") if p.exists()]
    files += sorted((root / ".agents" / "rules").glob("*.md"))
    rules = [read_rule(p) for p in files]
    active = [r for r in rules if r["trigger"] == "always_on"]
    total = sum(r["tokens"] for r in active)
    print(f"{'file':<28}{'trigger':<16}{'bytes':>8}{'tokens':>8}  note")
    for r in rules:
        note = []
        if r["bytes"] > FILE_LIMIT_BYTES: note.append("OVER 24,000 B -> truncated")
        if r["trigger"] == "INVALID": note.append("discarded (no/invalid frontmatter)")
        print(f"{r['path'].name:<28}{r['trigger']:<16}{r['bytes']:>8}{r['tokens']:>8}  {' / '.join(note)}")
    print(f"\nalways_on total: {total:,} tokens / budget {RULES_BUDGET_TOKENS:,}")
    if total <= RULES_BUDGET_TOKENS:
        print("OK: すべて本文のまま入ります"); return
    demoted, remaining = [], total
    for r in sorted(active, key=lambda r: r["tokens"], reverse=True):
        if remaining <= RULES_BUDGET_TOKENS: break
        demoted.append(r); remaining -= r["tokens"]
    print(f"over by {total - RULES_BUDGET_TOKENS:,}; demoted to pointers (largest first):")
    for r in demoted:
        print(f"  - {r['path'].relative_to(root)}: {r['desc'] or '(description なし)'}")
    print(f"inline after demotion: {remaining:,} tokens")
 
if __name__ == "__main__":
    main(sys.argv[1] if len(sys.argv) > 1 else ".")

pip install tiktoken を済ませ、ワークスペースの root を渡して python3 rules_budget_probe.py . と実行します。

なぜこう書いたかを二つだけ書き残します。取り込みの展開をサイズ判定の前に置いているのは、ドキュメントが「展開後のバイト数」で 24,000 を判定すると明記しているからです。trigger を四つの値と照合しているのは、綴りを誤ったルールがエラーにならず黙って捨てられるからで、これは後で実際に引っかかりました。

トークンの数え方は cl100k_base の近似で、Gemini の実カウントとは前後します。境界ぎりぎりの判断には向きませんが、「越えているか、どれくらい越えているか」を知るには十分でした。

数えた結果

私の Lab 運用ルール集は、要点をまとめた AGENTS.md と、参照表・共通手順・サイト固有の値・文体の決まり・チェックリストの 5 本で構成されています。全部を always_on として置いた場合の結果が次の表です。

ファイル役割バイトトークン(近似)
AGENTS.md運用の要点15,3925,685
content-core.md共通手順と検査21,9937,988
reference.md参照表19,9447,114
voice.md文体の決まり12,2804,593
site.mdサイト固有の値5,1661,830
checklist.md執筆チェックリスト3,6231,403
always_on の合計28,613

合計は 28,613 トークンで、枠を 8,613 越えておりました。試算では大きい順に content-core.md(7,988)と reference.md(7,114)がポインタに降ろされ、本文のまま残るのは 13,511 トークン分です。

ここで手が止まりました。降ろされる 2 本のうち content-core.md は、私がいちばん「常に読んでいてほしい」と思っていた手順書だったのです。一方で、3,623 バイトのチェックリストは何もしなくても本文のまま残ります。

枠は重要度を見ません。大きさだけを見ます。私が「重要だから詳しく書いた」文書ほど大きくなり、大きいものから順に降ろされる——分厚くした理由と、降ろされる理由が同じでした。

直感と逆だった三つのこと

試算を何度か回すあいだに、ドキュメントを読んだだけでは分からなかったことが三つありました。

一つ目は、上で書いた「大きい順」です。減らすなら、いちばん大切な文書を短くするのがいちばん効きます。

二つ目は、トークンが足りていてもバイトで切られる場合があることです。AGENTS.md の末尾に @[参照](./reference.md) を一行足して参照表を取り込む形にすると、展開後は 35,231 バイトで 24,000 を越え、切り詰めの対象になりました。トークンは 12,769 で 20,000 の枠には余裕があるのに、です。取り込みは便利ですが、取り込んだ分もそのファイルのバイト数に乗ります。

三つ目は、trigger: alwaysOn と書いた試しのファイルが、警告もなく一覧から消えたことです。ドキュメントに「黙って捨てられる」と書いてあるとおりで、私の道具でも最初は alwaysOn をそのまま表示してしまい、照合を足して初めて INVALID と出るようになりました。ルールが効いていない気がするときは、.antigravityignore が効いていないときに疑う4か所と同じで、まず綴りを疑うのが早いのだと思います。

落とされる前に、自分で降ろす

結果を見て、私は 2 本を自分で model_decision に変えました。content-core.md と reference.md の frontmatter を書き換え、description を「いつ開くべきか」が分かる一文に直しただけです。

同じ道具で数え直すと、always_on の合計は 13,511 トークンになり、枠まで 6,489 の余裕が生まれました。エージェントから見える形は、枠に降ろされた場合とほとんど同じです。違うのは、どれを降ろすかを枠ではなく私が決めた、という一点だけなのです。

枠に落とされる前に、自分で降ろす順番を決めておく。 この線引きだけは、ルール集を増やすたびに守るようにしています。

残す側にも基準を置きました。短い禁止事項と文体の決まりは always_on に残し、長い手順と参照表は description を丁寧に書いて model_decision に降ろします。手順は必要な場面が決まっているので、エージェントが開くべきときに開けば足りるのです。

同じ 2.17.0 では、リポジトリ単位の設定が .gemini/config.json に移り、旧 .agents/settings.json は読まれなくなりました。設定の置き場所が動いた直後は、無人実行のエージェントが設定ファイルを書き換えたら戻す、小さな番人スクリプトの組み方で書いた見張りの対象も、新しい場所へ向け直しておくと安心です。

まず一本だけ数えてみる

更新を入れる前に、上の道具をワークスペースの root で一度だけ走らせてみていただければと思います。合計が 20,000 を越えていたら、いちばん大きい一本の description を書き直して model_decision に降ろす——それだけで、枠に選ばれる代わりに自分で選んだことになります。

私も、いちばん大切だと思っていた手順書から降ろしました。降ろしてみると、エージェントは必要なときにちゃんとそれを開いてくれるのだと、いまは感じています。

シェア

お読みいただきありがとうございます

Antigravity Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥2,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

◉ Antigravity 基本2026-07-04
安定版と新機能版が並んだとき、個人運用ではどちらのビルドに乗るか
Antigravity が 2.0系(安定)と 2.1系(新機能)を並行公開したことで、個人開発者は「どちらに乗るか」を自分で決める必要が出てきました。自動処理を止めないためのビルド選定と切替の設計をまとめます。
◉ Antigravity 基本2026-07-03
2.0系と2.1系、どちらに乗るか — 無人運用と対話開発でビルド系列を分ける版管理ポリシー
2.1系と2.0系のビルドが一斉に並んだリリースページを前に、無人運用と対話開発でビルド系列を分ける版管理ポリシーを作りました。version-policy.json による宣言、毎朝のドリフト突合、2.1系の観察を2.0系へ反映する昇格手順を紹介します。
◉ Antigravity 基本2026-07-01
Antigravity 2.0 でチャット側に MCP が出てこないときに確認したこと
Antigravity 2.0 が IDE とチャット型エージェントの2アプリに分かれてから、IDE で設定した MCP サーバーがチャット側の一覧に出てこなくなりました。設定スコープが別々になっている原因と、ワークスペース単位に真実源を一本化して両アプリで揃える手順をまとめます。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます