既定の Flash モデルが Gemini 3.5 Flash に切り替わった日、私の手元では特に設定をいじらないまま、いくつかの定型タスクの返答が目に見えて速くなりました。同時に、込み入ったリファクタリングをひとつ任せたとき、以前なら一発で通っていた変更が二往復に増えました。
速くなった部分と、雑になった部分。どちらも同じモデル変更の裏表です。
ここで取れる態度は二つあります。ひとつは「速いのだから全部 Flash でいい」と割り切ること。もうひとつは「品質が落ちるのは困るから、結局 Pro に戻す」こと。私はどちらも一度ずつ試して、どちらも無駄が出ると感じました。前者は精度が必要な場面で手戻りが増え、後者は単純な置換にまで重いモデルの待ち時間を払うことになります。
落としどころは、タスクごとに使い分けることでした。問題は「どう使い分けるか」を勘ではなく、再現できる基準にすることです。
速さと正しさは別の軸で測る
モデルを比べるとき、つい「どちらが賢いか」という一本の物差しで考えてしまいます。けれど実際の開発では、賢さよりも「このタスクに、その賢さが要るのか」のほうが効きます。
私自身は二つの軸でタスクを見るようにしました。個人開発では、ここを言葉にできるかどうかが、後々の積み重ねを左右します。
ひとつは決定の重さです。間違えたときの手戻りがどれくらい大きいか。変数名の一括置換なら、間違っても一目で気づいて直せます。状態管理の設計を変えるような変更は、間違いが下流に伝播して、気づくのが遅れます。
もうひとつは文脈の広さです。判断にどれだけの周辺コードを同時に見る必要があるか。一ファイル内で閉じるタスクと、複数ファイルの依存関係を頭に入れて進めるタスクとでは、必要な「視野」がまるで違います。
この二軸で並べると、Flash が得意な領域と Pro に任せたい領域が、はっきり分かれて見えてきます。
振り分けの判定表
実際に手元で運用している判定を、表の形に整理します。文脈の広さを横、決定の重さを縦に取っています。
軽い決定 × 狭い文脈 → Flash。整形、命名の置換、コメント追記、定型的なテスト雛形の生成。ここは速度がそのまま体感に効きます。
軽い決定 × 広い文脈 → Flash。複数ファイルにまたがる文字列置換や、import の整理など、判断は単純でも対象が散らばるもの。視野は要りますが、間違えても安全です。
重い決定 × 狭い文脈 → Pro。一ファイルの中でも、非同期処理の境界やエラーハンドリングの設計など、間違えると静かに壊れるもの。範囲は狭くても、ここで節約すると後で高くつきます。
重い決定 × 広い文脈 → Pro。アーキテクチャの変更、データの流れの組み替え、複数モジュールの整合を取る作業。Flash に任せて二往復するより、最初から Pro で一度に通したほうが速いことが多いです。
この表のいいところは、迷ったときに「これは間違えたら痛いか」「どれだけ広く見る必要があるか」の二つだけ自問すればよい点です。モデルの内部性能を覚える必要はありません。
私の場合、日々のタスクを数えると、件数では約70%が Flash 側、Pro 側は約30%に落ち着きました。Pro 側は一件あたりの所要時間が Flash 側のおよそ2倍に達します。一方で、開発時間に占める割合で見ると逆転して、Pro 側のタスクが体感の半分以上を占めます。重い仕事は件数こそ少なくても、一件あたりに時間がかかるからです。この非対称は、振り分けの効果を考えるうえで大事な感覚です。
ルーティングを設定に落とす
判定表を頭の中に置いておくだけでも効果はありますが、毎回どちらを選ぶか手で決めるのは続きません。Antigravity のサブエージェント定義でモデルを固定し、タスクの入口で行き先を分けてしまうほうが、判断のぶれが減ります。
決まった種類の作業ごとに、使うモデルを宣言で固定する考え方です。
# .antigravity/agents.yaml
# タスク種別ごとにモデルを固定し、入口で振り分ける
agents :
quick-edit :
model : gemini-3.5-flash
description : 整形・命名置換・コメント追記など、間違えても安全な軽作業
temperature : 0.1
max_context_files : 8
bulk-refactor :
model : gemini-3.5-flash
description : 複数ファイルの機械的な置換。広い視野は要るが判断は単純
temperature : 0.1
max_context_files : 40
design-change :
model : gemini-3.5-pro
description : 非同期境界・エラー設計・状態管理など、間違いが下流に伝播する変更
temperature : 0.2
max_context_files : 24
architecture :
model : gemini-3.5-pro
description : データフロー再設計・モジュール整合。一度で通したい重い仕事
temperature : 0.3
max_context_files : 80
ここで temperature を Flash 側で低く抑えているのには理由があります。軽作業ほど「いつ実行しても同じ結果が返る」ことに価値があります。整形のたびに別の体裁を提案されては、差分が読みにくくなるだけです。逆にアーキテクチャ側を少し上げているのは、設計の検討では複数の案を出してもらったほうが議論の足しになるからです。
max_context_files を種別ごとに変えているのも、無駄な読み込みを抑えるためです。quick-edit に 80 ファイルを読ませる意味はありませんし、architecture に 8 ファイルしか見せなければ、肝心の整合判断ができません。視野の広さと種別を対応させておくと、Flash 側のタスクが余計な文脈を抱えて遅くなるのを防げます。
入口での振り分けは、タスクの言い回しから機械的に決めても十分実用になります。
# route.py — タスク記述から行き先エージェントを決める最小ルーター
import re
# 「重い決定」を示唆する語。ここに当たれば Pro 側へ寄せる
HEAVY_SIGNALS = (
"設計" , "アーキテクチャ" , "状態管理" , "非同期" , "エラーハンドリング" ,
"データフロー" , "リファクタ" , "整合" , "マイグレ" , "並行" ,
)
# 「広い文脈」を示唆する語
WIDE_SIGNALS = ( "複数ファイル" , "全体" , "横断" , "依存関係" , "モジュール間" )
def route (task: str ) -> str :
heavy = any (s in task for s in HEAVY_SIGNALS )
wide = any (s in task for s in WIDE_SIGNALS )
if heavy and wide:
return "architecture"
if heavy:
return "design-change"
if wide:
return "bulk-refactor"
return "quick-edit"
if __name__ == "__main__" :
samples = [
"変数名を userId から accountId に一括置換したい" ,
"認証フローのエラーハンドリングを設計し直したい" ,
"複数ファイルにまたがる import を整理したい" ,
"状態管理をモジュール間で整合するよう再設計したい" ,
]
for s in samples:
print ( f " { route(s) :16 } <- { s } " )
この程度のルーターでも、日々のタスクの大半は正しく振り分けられます。語の一致で迷う境界例だけ手で上書きすればよく、完璧を目指す必要はありません。むしろ、ルールを単純に保つほうが、なぜその行き先になったのかを自分で説明できて安心です。
差し替えで品質が落ちていないかを確かめる
既定モデルが変わると、これまで Flash 相当で問題なく回っていたタスクが、静かに精度を落とすことがあります。速さに気を取られていると、この劣化は見落とされがちです。これは本番運用でじわじわ効いてくる注意点で、気づいた時点で閾値を見直せば回避できます。
そこで私は、代表的なタスクをいくつか固定の入力として持っておき、モデルを変えたときに出力がどれだけ動いたかを並べて見るようにしています。合否を自動で決めるのではなく、人が差分を眺めて判断するための、軽い比較です。
# compare.py — 同じ入力を2モデルに渡し、出力の差分を並べる
import subprocess
import difflib
import sys
# 普段よく回す代表タスク。プロジェクトに合わせて数件選ぶ
CASES = [
"この関数に型注釈を付けてください" ,
"このコンポーネントのpropsをinterfaceに切り出してください" ,
"このエラーハンドリングを早期returnに整理してください" ,
]
def run (model: str , prompt: str , fixture: str ) -> str :
# antigravity CLI を非対話で呼ぶ想定。環境に合わせて置き換える
result = subprocess.run(
[ "antigravity" , "run" , "--model" , model, "--input" , fixture, "--prompt" , prompt],
capture_output = True , text = True , timeout = 120 ,
)
return result.stdout
def main (fixture: str ):
for prompt in CASES :
a = run( "gemini-3.5-flash" , prompt, fixture)
b = run( "gemini-3.5-pro" , prompt, fixture)
diff = list (difflib.unified_diff(
a.splitlines(), b.splitlines(),
fromfile = "flash" , tofile = "pro" , lineterm = "" ,
))
changed = len (diff)
mark = "差分大" if changed > 40 else "差分小"
print ( f "[ { mark } / { changed } 行] { prompt } " )
if changed > 40 :
print ( " \n " .join(diff[: 30 ]))
print ( "..." )
if __name__ == "__main__" :
main(sys.argv[ 1 ] if len (sys.argv) > 1 else "fixtures/sample.ts" )
このスクリプトは合否を出しません。出すのは「どのタスクで Flash と Pro の出力が大きく食い違ったか」だけです。差分が小さいタスクは Flash に任せて安心できますし、差分が大きいタスクは、判定表を見直して Pro 側に寄せる候補になります。
私はこれを、既定モデルが更新されたという告知を見たときに一度だけ回しています。毎日動かす種類のものではありません。モデルが変わる節目に、自分の振り分けがまだ妥当かを確かめる、点検のための道具です。実際、3.5 Flash への切り替えのときは、型注釈の付与は差分が小さく Flash のままで問題なし、エラーハンドリングの整理は差分が大きく、design-change 側へ移すきっかけになりました。
どこから始めるか
すべてのタスクに最適なモデルを割り当てようとすると、設定が複雑になって続きません。私は、次の順序で始めることを推奨します。
まず「重い決定 × 広い文脈」の象限だけを Pro に固定します。
残りはすべて既定の Flash に任せます。
運用して取りこぼしを感じた象限を、後から一つずつ Pro 側へ動かします。
この一手だけで、手戻りが痛いタスクを取りこぼさず、かつ日常の大半を速いモデルで回せます。振り分けの精度を上げるのは、運用してみて「ここは取りこぼした」と感じた象限を、後から一つずつ動かしていけば十分です。
モデルが速くなったこと自体は歓迎すべき変化です。その速さを、どこで受け取り、どこで品質と引き換えにしないかを自分で決められるようにしておく。それが、既定モデルが動いても慌てずに済む準備だと考えています。