Antigravity を使い始めた頃の私は、クラウド型の Gemini や Claude にだけ繋いで使っていました。当時はそれで十分だと思っていたのですが、ある日、機密性の高い社内文書を扱う仕事を引き受けたとき、状況が変わりました。クラウド LLM に流せないデータが目の前にあり、「Antigravity 自体は手放したくないけれど、推論はローカルでやりたい」というニーズが生まれたのです。
調べてみると Antigravity は思いの外柔軟で、Ollama や LM Studio を介してローカルの推論サーバーに繋ぐ設定が可能です。クラウド LLM のような「ワンクリックで万能」ではないのですが、設定さえ詰めれば実用に耐える組み合わせになります。
私が試行錯誤しながら詰めた設定と、運用してわかったクラウド LLM との使い分け、そしてローカル LLM ならではの制約をどう回避するかを実例とともに整理しました。「興味はあるが面倒くさそう」と感じていた方の背中を押せる内容を意識しました。
なぜ Antigravity でローカル LLM を使うのか
そもそもの動機を整理すると、私の場合は大きく 3 つあります。
ひとつは、機密データの扱いです。クライアントの NDA で「外部 API に送信不可」と書かれているデータを Antigravity で扱いたい場面があります。クラウド LLM だと運用ポリシー的に難しい局面でも、ローカルで完結すれば許容されるケースは多いです。これが最も切実な動機でした。
ふたつめは、コストです。1 日に何百回も呼ぶような自動化を組むと、API コストが地味に積み上がります。手元のマシンに余力があるなら、定型作業はローカル LLM に任せて、難しい判断だけクラウドに振る、という分担が経済的です。
3 つめは、オフライン耐性です。これは些細に思えますが、ネットワーク環境が不安定な場所で作業することがある人には大きな価値があります。私は時々海外で作業するのですが、現地のネットが遅かったり API が遅延したりするとき、ローカル LLM があると安心できます。
逆に「ローカル LLM じゃなくていい」場面もあります。最先端の推論力が必要なタスク(複雑な設計判断、長文の論理整合性チェックなど)はクラウド一択です。手元のマシンで動く範囲のモデルでは、そこまでの能力には到達しません。
Ollama と LM Studio、どちらを選ぶか
Antigravity から繋ぐローカル推論サーバーの代表格は、Ollama と LM Studio です。私は両方使ってきましたが、用途で使い分けています。
Ollama の強みは「コマンドライン中心で安定」しているところです。CLI で ollama pull gemma3:27b のようにモデルを取得し、ollama serve で OpenAI 互換の API を立てる、というシンプルな構造です。バックグラウンドで動かしっぱなしにしておく運用に向いています。私は Mac mini にこれを常駐させて、自宅 LAN 内のあらゆる端末から叩いています。
LM Studio の強みは「GUI で完結する」ところです。モデルのダウンロード、量子化レベルの選択、システムインストラクションの試行錯誤、すべてが画面上で完結します。プロンプトをいじりながら挙動を見比べるような探索フェーズには LM Studio が明確に楽です。一方で、運用フェーズに入ると Ollama の方が安定します。
私の使い分けは、「手元で試行錯誤するときは LM Studio、運用に乗せるときは Ollama」です。同じ GGUF モデルなら両方で動かせるので、移行コストは低いです。
接続設定の実際 — Ollama 編
Ollama を Antigravity から呼ぶ場合の設定を共有します。Antigravity の設定画面で、カスタムプロバイダとして OpenAI 互換エンドポイントを追加します。
Provider Name: Ollama (local)
Base URL: http://localhost:11434/v1
API Key: ollama (任意の文字列で OK)
Model: gemma3:27b (事前に ollama pull で入れたモデル名)
Base URL の末尾に /v1 を付けるのが地味に重要です。これを忘れると、Antigravity 側が OpenAI Chat Completions の形式でリクエストを投げたときに 404 が返ってきます。
API Key は Ollama では実際には使われませんが、Antigravity 側のバリデーションが「空欄」を弾くことがあるので、適当な文字列を入れておきます。ollama でも dummy でも構いません。
別のマシンで動いている Ollama に繋ぐ場合は、Ollama 側で外部接続を許可する必要があります。デフォルトは localhost のみなので、OLLAMA_HOST=0.0.0.0:11434 ollama serve のように起動します。同じ LAN 内であれば、これで他のマシンの Antigravity から http://192.168.1.10:11434/v1 のようにアクセスできます。
セキュリティの観点から、外部 LAN や WAN に開放するのは避けてください。最低でも VPN や SSH トンネル経由で繋ぐ運用にしないと、誰でもあなたのマシンの推論を使い放題になります。
接続設定の実際 — LM Studio 編
LM Studio の場合は、アプリ内の "Local Server" タブから API サーバーを起動します。デフォルトのポートは 1234 で、こちらも OpenAI 互換 API を提供します。
Provider Name: LM Studio (local)
Base URL: http://localhost:1234/v1
API Key: lm-studio
Model: ローカルロード中のモデル名
LM Studio の便利な点は、サーバー起動時のシステムインストラクションを GUI で設定できることです。日本語応答を強制したい場合、量子化を工夫したい場合など、Antigravity 側のプロンプトを変えずに挙動を調整できます。
ただし LM Studio の API サーバーはアプリを起動している間しか動かないので、長時間運用には向きません。「日中は LM Studio で実験、夜間バッチは Ollama」のような分担をしている方を多く見ます。
ローカル LLM 向けのモデル選定
Antigravity から呼ぶ用途で、私が現在使っているモデルを共有します。お使いのマシンスペックによって最適解は変わりますが、参考にしてください。
メインで使っているのは Gemma 3 27B です。M2 Pro の Mac mini(メモリ 32GB)で量子化版(Q4_K_M)が快適に動きます。コード補完、文章校正、要約などの一般タスクは、これでクラウド LLM に近い品質が得られます。
長文を扱うときは Llama 3.3 70B の量子化版を使います。ただし速度は明確に遅くなるので、対話的な使い方には向きません。バッチ処理でじっくり推論させる用途専用です。
軽量タスクには Phi-3.5 mini(3.8B)を使います。ファイル名の正規化、簡単な分類、JSON の整形といった「機械的な変換」用途なら、これで十分高速です。Antigravity の補助的なエージェントとして常時走らせるならこのクラスが現実的です。
モデル選びで失敗しやすいのは、「とにかく大きいのを入れる」発想です。メモリギリギリのモデルを使うと、推論中にスワップが発生して激遅になります。マシンの実効メモリに対して 70% 以下のモデルサイズを選ぶのが安全です。
クラウド LLM との使い分けルーティング
Antigravity の魅力のひとつは、複数のプロバイダを共存させられることです。私はプロジェクトごとに、どのプロバイダを使うかをルール化しています。
機密データを含むプロジェクトでは、すべての推論を Ollama 経由のローカル LLM に固定します。Antigravity の設定でデフォルトプロバイダを Ollama にしておくと、うっかりクラウドに送る事故を防げます。
逆に、機密性の低い OSS プロジェクトでは、デフォルトをクラウド LLM(Gemini や Claude)にして、コード補完など軽量タスクだけローカルに振ります。これでコスト最適化と品質を両立できます。
プロジェクト横断で使い分けるテクニックとして、Antigravity のプロジェクト設定ファイル(.antigravity/config.json のような場所、バージョンによって異なる)に defaultProvider を書き込んでおくのが便利です。プロジェクトを開くたびに正しいプロバイダが自動選択されます。
ローカル LLM の制約 1: コンテキスト長
ローカル LLM の代表的な制約は、コンテキスト長の短さです。クラウド LLM は 1M トークンといった巨大なコンテキストをサポートしますが、ローカル LLM は 8k〜32k 程度が一般的です。Gemma 3 で 128k まで拡張できるモデルもありますが、メモリ消費が爆発するので実用範囲は限られます。
対処パターンは 3 つです。
ひとつは、Antigravity 側のコンテキスト分割機能に任せることです。長いファイルを開いて推論させる場合、Antigravity が「関連部分だけ抜き出してモデルに渡す」モードがあります。これを有効にしておくと、ローカル LLM のコンテキスト制限内に収まりやすくなります。
ふたつめは、要約をかましてからローカル LLM に渡すことです。長い議事録の要約タスクなら、まずクラウド LLM で章ごとに要約してから、それをローカル LLM で統合する、という二段構えにします。Antigravity 上で別々のプロバイダを使い分けられるので、これは比較的簡単に組めます。
3 つめは、コンテキスト依存度の低いタスクをローカル LLM に集中させることです。たとえば「このコードのレビュー」よりも「この関数だけのリファクタリング」の方がコンテキストは短くて済みます。タスク粒度を意識すれば、コンテキスト制約は意外と気にならなくなります。
ローカル LLM の制約 2: 関数呼び出しの精度
クラウド LLM の Function Calling は近年急速に賢くなりましたが、ローカル LLM ではまだ品質に差があります。Gemma 3 27B でも、複雑なツールチェーンをミスなく実行するのは厳しいです。
対処は 2 つあります。ひとつは、ローカル LLM に複雑な関数呼び出しをさせないことです。エージェント的な使い方は基本クラウドに任せて、ローカルは「単発の応答生成」に絞ります。
もうひとつは、関数呼び出し代替のフォーマットを使うことです。OpenAI 形式の Function Calling ではなく、「JSON 出力を強制するプロンプト」で代替します。「以下の形式の JSON だけを出してください: { ... }」と書いて、レスポンスを自分でパースする方式です。これなら多くのローカルモデルで安定動作します。
Antigravity のプラグインを書いている方なら、ローカル LLM 用の薄いラッパーを作っておくと便利です。「JSON 出力 → パース → 関数実行」までを 1 つのプラグインで処理する感じですね。
JSON 出力を強制する薄いラッパーを、実際に書く
「JSON 出力を強制して自分でパースする」と書きましたが、素朴に実装すると必ず破綻します。モデルは前置きを付けたがりますし、コードフェンスで囲みたがります。json.loads に生の応答をそのまま渡す実装は、体感で 1 割前後は落ちます。夜間バッチで 1 割落ちると、朝には後始末が待っています。
私が Ollama 経由の定型処理に挟んでいるラッパーは、次の 3 つだけをやります。Ollama の format にスキーマを渡して構造を縛る。それでも壊れたら、壊れた出力そのものをモデルに見せて 1 回だけ直させる。それでも駄目なら諦めて呼び出し側に返す。
# local_json.py — ローカルモデルから構造化データを安全に受け取る
import json
import urllib.request
from typing import Any
BASE_URL = "http://localhost:11434"
MODEL = "gemma3:27b"
class LocalJSONError ( RuntimeError ):
"""修復リトライ後も構造化できなかった場合"""
def _chat (messages: list[ dict ], schema: dict | None ) -> str :
payload: dict[ str , Any] = {
"model" : MODEL ,
"messages" : messages,
"stream" : False ,
"options" : { "temperature" : 0 },
}
if schema is not None :
# Ollama はここに JSON Schema を渡すと、その形に沿った出力を強制します
payload[ "format" ] = schema
req = urllib.request.Request(
f " { BASE_URL } /api/chat" ,
data = json.dumps(payload).encode(),
headers = { "Content-Type" : "application/json" },
)
with urllib.request.urlopen(req, timeout = 120 ) as res:
return json.loads(res.read())[ "message" ][ "content" ]
FENCE = "`" * 3 # 記事中での表記の都合で組み立てています
def _salvage (text: str ) -> str :
"""コードフェンスや前置きが付いていても中身を取り出す"""
if FENCE in text:
text = text.split( FENCE )[ 1 ]
if text.startswith( "json" ):
text = text[ 4 :]
start = min ((i for i in (text.find( "{" ), text.find( "[" )) if i != - 1 ), default =- 1 )
if start == - 1 :
return text.strip()
end = max (text.rfind( "}" ), text.rfind( "]" ))
return text[start:end + 1 ].strip() if end > start else text[start:].strip()
def ask_json (prompt: str , schema: dict , * , repair: bool = True ) -> Any:
messages = [{ "role" : "user" , "content" : prompt}]
raw = _chat(messages, schema)
try :
return json.loads(_salvage(raw))
except json.JSONDecodeError as first_error:
if not repair:
raise LocalJSONError( f "JSON として読めません: { raw[: 120 ] } " ) from first_error
# 壊れた出力そのものを見せて直させる。指示だけを繰り返しても直りません
messages += [
{ "role" : "assistant" , "content" : raw},
{ "role" : "user" , "content" :
f "上の出力は JSON として解析できませんでした( { first_error.msg } )。"
"説明や前置きを一切付けず、有効な JSON だけを出力し直してください。" },
]
retried = _chat(messages, schema)
try :
return json.loads(_salvage(retried))
except json.JSONDecodeError as second_error:
raise LocalJSONError(
f "修復リトライ後も失敗しました: { retried[: 120 ] } "
) from second_error
if __name__ == "__main__" :
schema = {
"type" : "object" ,
"properties" : {
"category" : { "type" : "string" ,
"enum" : [ "bug" , "feature" , "question" , "other" ]},
"confidence" : { "type" : "number" },
"reason" : { "type" : "string" },
},
"required" : [ "category" , "confidence" ],
}
result = ask_json(
"次のレビューを分類してください。出力は JSON のみ。 \n "
"『起動直後に落ちます。再インストールしても同じです』" ,
schema,
)
print (result)
効いているのは、次の 3 点です。
format にスキーマを渡す — 第一の防波堤です。プロンプトで「JSON だけ出して」と頼むより桁違いに安定します。enum で候補を絞ると、表記ゆれ(Bug と bug が混ざる類)もそこで止まります
_salvage() で切り出す — 第二の防波堤です。スキーマを渡してもなお、前置きやコードフェンスが混ざることがあります。最初の { から最後の } までを切り出すだけの素朴な処理ですが、これがあるだけで失敗が目に見えて減りました
壊れた出力そのものを会話に含めて渡す — 修復リトライの肝です。同じ指示をもう一度投げ直すだけでは、同じ壊れ方を繰り返します。自分の出力を見せられると、そこそこの確率で直してきます
リトライは 1 回までに固定しています。2 回目で直らない入力は、たいてい 3 回目でも直りません。時間を溶かすより、例外にして呼び出し側でクラウドへ回すほうが速いです。私は分類のような軽い処理では、この例外を受けたらそのままクラウドへフォールバックさせています。ローカルで 9 割方を捌き、こぼれた分だけクラウドが拾う。この形が、いまのところ一番手がかかりません。
ローカル LLM の制約 3: 速度
最後の制約は、純粋な推論速度です。Gemma 3 27B を Mac mini M2 Pro で動かすと、トークン生成速度は秒間 12〜18 トークン程度です。クラウド LLM の数倍は遅いので、対話的な使い方ではストレスを感じます。
対処は、用途を分けることに尽きます。リアルタイム性が必要な作業(コード補完、対話)はクラウド LLM、バッチ的な作業(夜間の自動更新、ログ要約、コンテンツ生成)はローカル LLM、と切り分けます。
私のサイト群の自動更新は深夜に走らせているので、多少遅くても困りません。ローカル LLM は深夜の静かな時間帯に活躍してくれて、API コストを抑えてくれる、という分担が定着しています。
公開ベンチマークではなく、手元の数字で乗り換えを決める
ローカル LLM の世界では、新しいモデルが数週間おきに降りてきます。そのたびに「乗り換えるべきか」で迷うのですが、公開ベンチマークのスコアは判断材料としてほとんど役に立ちませんでした。あれは高性能な GPU 環境での数字であり、私の Mac mini で Q4 量子化して動かしたときの体感とは別物です。
そこで、モデルを入れ替えるたびに同じ条件で測る小さなベンチを用意しました。見るのは 2 つだけです。最初のトークンが返るまでの時間(TTFT)と、生成中のトークン毎秒。前者は対話の待ち時間、後者はバッチ処理の総所要時間に直結します。
# bench_local.py — Ollama / LM Studio の OpenAI 互換エンドポイントを実測する
import json, statistics, time
import urllib.request
BASE_URL = "http://localhost:11434/v1" # LM Studio なら http://localhost:1234/v1
API_KEY = "ollama" # 中身は使われないが空欄は弾かれる
MODEL = "gemma3:27b"
RUNS = 5
PROMPT = (
"次の関数の役割を3文で説明してください。 \n\n "
"def merge(a, b): \n return {**a, **b} \n "
)
def one_run () -> tuple[ float , float , int ]:
"""1回の生成を計測し (TTFT秒, 生成秒, 出力トークン数) を返す"""
body = json.dumps({
"model" : MODEL ,
"messages" : [{ "role" : "user" , "content" : PROMPT }],
"stream" : True ,
"options" : { "temperature" : 0 , "seed" : 42 }, # 比較のため固定
}).encode()
req = urllib.request.Request(
f " { BASE_URL } /chat/completions" ,
data = body,
headers = { "Content-Type" : "application/json" ,
"Authorization" : f "Bearer { API_KEY } " },
)
start = time.perf_counter()
ttft = None
tokens = 0
with urllib.request.urlopen(req) as res:
for raw in res:
line = raw.decode().strip()
if not line.startswith( "data: " ):
continue
payload = line[ 6 :]
if payload == "[DONE]" :
break
delta = json.loads(payload)[ "choices" ][ 0 ][ "delta" ]
if not delta.get( "content" ):
continue
if ttft is None :
ttft = time.perf_counter() - start # 最初の1トークン
tokens += 1
total = time.perf_counter() - start
return ttft, total - ttft, tokens
def pct (values: list[ float ], p: float ) -> float :
ordered = sorted (values)
return ordered[ min ( int ( len (ordered) * p), len (ordered) - 1 )]
if __name__ == "__main__" :
one_run() # 初回はモデルのロードが混ざるので捨てる
ttfts, rates = [], []
for i in range ( RUNS ):
ttft, gen_sec, tokens = one_run()
ttfts.append(ttft)
rates.append(tokens / gen_sec if gen_sec > 0 else 0 )
print ( f "run { i + 1 } : TTFT { ttft :.2f } s / { rates[ - 1 ] :.1f } tok/s / { tokens } tokens" )
print ( f " \n{ MODEL } " )
print ( f " TTFT p50 { statistics.median(ttfts) :.2f } s p95 { pct(ttfts, 0.95 ) :.2f } s" )
print ( f " tok/s p50 { statistics.median(rates) :.1f } p95(遅い側) { pct(rates, 0.05 ) :.1f } " )
options で temperature と seed を固定しているのは、出力長のばらつきで tok/s が揺れるのを抑えるためです。初回の実行を捨てているのは、モデルのロード時間が TTFT に混入するからです。ここを捨てないと、最初の 1 回だけ TTFT が数十秒になり、平均が壊れます。
tok/s だけは p95 ではなく下位 5% を見ています。速い側の外れ値は嬉しいだけで、判断には要らないためです。遅い側がどこまで落ちるかが、バッチの所要時間を決めます。
私の Mac mini M2 Pro(メモリ 32GB)で測ると、おおむね次のような数字に落ち着きます。
モデル / 量子化 TTFT p50 tok/s p50 tok/s 下位5% 向く用途
27B クラス / Q4_K_M 約 1.2 秒 12〜18 9 前後 夜間バッチ・要約・校正
27B クラス / Q8 約 2 秒超 1 桁台に低下 計測が不安定 32GB では非推奨(スワップ発生)
4B クラス / Q4_K_M 0.3 秒未満 60 超 40 台 分類・整形・常駐の補助
この表で一番伝えたいのは 2 行目です。同じ 27B でも Q8 に上げた瞬間、実効メモリを超えてスワップが始まり、数字が別世界になります。前述の「実効メモリの 70% 以下」という目安は、この計測から逆算した経験則です。品質を上げようとして量子化を緩めた結果、速度が 1 桁落ちて使わなくなる、という遠回りを私自身が一度しています。
27B クラスを選ぶ場合は、量子化を Q4_K_M に留めておくのが無難です。新しいモデルを入れたら、まずこのスクリプトを流します。数字が前のモデルと同等以上で、かつ出力の質が落ちていなければ乗り換える。それだけの判断で十分に回っています。
ハードウェアの現実的な選択肢
Antigravity でローカル LLM を本気で使うなら、ハードウェアの選択は避けて通れません。私の経験から、3 つの価格帯で推奨を書いておきます。
一番安い選択肢は、いま持っている M シリーズ Mac をそのまま使うことです。MacBook Pro M3 Pro 以上、メモリ 32GB 以上なら、Gemma 3 27B クラスを実用的に動かせます。新規投資不要で始められます。
中価格帯の専用機としては、Mac mini M2 Pro / M4 Pro のメモリ 32〜64GB が定番です。30 万円前後で、24 時間稼働できる推論サーバーが手に入ります。私はこれを使っています。
高価格帯では、Apple Silicon の Mac Studio(メモリ 96GB 以上)か、NVIDIA RTX 4090 / 5090 を積んだ Linux ワークステーションです。70B クラスのモデルを快適に動かしたいならこの帯域です。100 万円前後の投資になりますが、月のクラウド LLM コストが 5 万円を超えるような使い方をしているなら、半年〜1 年で回収できます。
ローカルとクラウドの損益分岐を、月額で見積もる
ハードウェアの価格帯を並べましたが、「で、自分は買うべきなのか」に答えないと選べません。私が Mac mini を推論サーバーに仕立てる前にやったのは、次の 3 つの数字を月額に揃える作業でした。
まず、ローカル側の月額。本体価格を償却期間で割り、電力費を足します。24 時間動かしっぱなしでも、推論していない時間の消費は小さいので、実測に近い平均値を使います。
次に、クラウド側の月額。ここは「1 日の呼び出し回数 × 1 回あたりの平均トークン数 × 単価」で出ます。多くの人が入力トークンだけで見積もって外すので、出力側も必ず足します。
最後に、ローカルに流せる割合。ここが要点です。全部をローカルに移せるなら話は単純ですが、実際には難しい判断はクラウドに残ります。私の場合、回数ベースで 7 割前後がローカルに移せました。
# breakeven.py — ローカル推論サーバーとクラウド API の月額を突き合わせる
from dataclasses import dataclass
@dataclass
class Local :
hardware_jpy: int # 本体価格
amortize_months: int # 償却期間(私は36ヶ月で見ています)
watt_avg: float # 平均消費電力(W)
jpy_per_kwh: float = 31.0
def monthly (self) -> float :
depreciation = self .hardware_jpy / self .amortize_months
power = self .watt_avg * 24 * 30 / 1000 * self .jpy_per_kwh
return depreciation + power
@dataclass
class Cloud :
calls_per_day: int
in_tokens: int # 1回あたり平均入力トークン
out_tokens: int # 1回あたり平均出力トークン
in_jpy_per_1k: float
out_jpy_per_1k: float
def monthly (self, offload_ratio: float = 0.0 ) -> float :
"""offload_ratio: ローカルへ逃がす呼び出しの割合"""
calls = self .calls_per_day * 30 * ( 1 - offload_ratio)
per_call = ( self .in_tokens / 1000 * self .in_jpy_per_1k
+ self .out_tokens / 1000 * self .out_jpy_per_1k)
return calls * per_call
def report (local: Local, cloud: Cloud, offload: float ) -> None :
cloud_only = cloud.monthly( offload_ratio = 0.0 )
hybrid = local.monthly() + cloud.monthly( offload_ratio = offload)
diff = cloud_only - hybrid # 正なら併用が安い
print ( f "クラウドのみ : { cloud_only :>10,.0f } 円/月" )
print ( f "ローカル+クラウド併用: { hybrid :>10,.0f } 円/月"
f "(うちハード { local.monthly() :,.0f } 円)" )
print ( f "差分 : { diff :>+10,.0f } 円/月" )
# 逆転点: ローカルへ逃がした分の削減額が、ハードの月額と釣り合う呼び出し量
per_call = (cloud.in_tokens / 1000 * cloud.in_jpy_per_1k
+ cloud.out_tokens / 1000 * cloud.out_jpy_per_1k)
breakeven_calls = local.monthly() / ( 30 * offload * per_call)
print ( f "逆転点 : 1日あたり約 { breakeven_calls :,.0f } 回"
f "(現状 { cloud.calls_per_day } 回)" )
if diff > 0 :
print ( f "→ 併用が有利。 { local.hardware_jpy / diff :.1f } ヶ月で本体価格を回収します" )
else :
print ( "→ 現状の呼び出し量では、費用面だけでは正当化できません" )
if __name__ == "__main__" :
mac_mini = Local( hardware_jpy = 300_000 , amortize_months = 36 , watt_avg = 25 )
api = Cloud( calls_per_day = 400 , in_tokens = 3_000 , out_tokens = 800 ,
in_jpy_per_1k = 0.45 , out_jpy_per_1k = 1.80 )
report(mac_mini, api, offload = 0.7 )
単価は必ずお使いのモデルの公式価格表で置き換えてください。上の数字は形を示すための仮値です。
このパラメータ(1 回あたり入力 3,000 / 出力 800 トークン、実効単価 2.79 円)で 3 つの帯を流すと、次のようになりました。
使い方 1日の呼び出し クラウドのみ 併用(7割ローカル) 本体の回収
試しに触る程度 20 回 1,674 円/月 9,394 円/月 回収に至らない
自動化を日常的に回す 400 回 33,480 円/月 18,935 円/月 20.6 ヶ月
夜間バッチが主力 2,000 回 167,400 円/月 59,111 円/月 2.8 ヶ月
計算してみて意外だったのは、逆転点が 1 日 152 回 と、思ったより低い位置にあったことです。感覚的には「数千回級でないと元は取れない」と思い込んでいたのですが、入力 3,000 トークンの呼び出しを日に 150 回も投げていれば、もう釣り合っています。
ただし、この逆転点は平均プロンプト長にそのまま引きずられます。同じ計算で入力 300 / 出力 100 トークンに落とすと、1 回あたりの単価が 0.32 円まで下がり、逆転点は 1 日 1,300 回台まで跳ね上がります。呼び出し回数ではなく、流しているトークン量で判断してください。 ここを回数だけで見て、私は最初に見積もりを一桁外しました。
そしてもう一点、表の 2 行目には注意が要ります。20.6 ヶ月での回収は、償却期間 36 ヶ月の内側には収まっています。ただ、ローカル LLM の世代交代の速さを考えると、2 年近くかけて回収する計画は、私はあまり信用していません。その頃には要求メモリの違うモデルが主流になっている可能性が高いからです。回収が 1 年を切る帯(表の 3 行目)に届いているかどうかを、私は実質的な線引きにしています。
裏を返せば、真ん中の帯にいる方は費用で決めなくてよい、ということでもあります。私が Mac mini を買った理由も、月額の差ではなく「外部 API に送れないデータを扱えるようになる」という一点でした。金額だけで迷っている段階なら、たいてい急いで買わなくて大丈夫です。
実運用で気をつけているモニタリング
ローカル LLM をプロダクションに乗せるなら、最低限のモニタリングは必須です。私が見ているのは次の指標です。
GPU/Neural Engine の温度と利用率。長時間の高負荷運用では、サーマルスロットリングで急に推論が遅くなることがあります。asitop のようなツールで定期的にチェックします。
メモリ使用量とスワップ発生量。スワップが発生し始めたら推論速度が桁違いに落ちるので、その閾値を超えないモデルサイズに調整します。
応答時間の中央値と 95 パーセンタイル。中央値だけ見ていると、たまに発生する遅延を見落とします。95 パーセンタイルを見ると、ユーザー体感の悪さがわかります。
これらを Grafana や簡易ダッシュボードで可視化しておくと、いざ問題が起きたときに原因の切り分けが速くなります。
ローカルモデルに切り替えた瞬間、応答が英語になる問題
ローカル LLM を使い始めて最初に戸惑うのが、クラウドモデルでは日本語で返っていたタスクサマリが、Ollama に切り替えた途端に英語になることです。
これは Antigravity 側の設定が効いていないのではなく、軽量モデルがシステム指示の言語指定を無視しやすい ために起こります。Gemma 3 4B クラスでは、システム指示に「日本語で回答する」と一行書くだけでは足りず、指示の位置とプロンプト内の言語比率に引きずられます。
個人開発の環境で試行錯誤しながら、最終的に安定させられた方法は次の二つです。
システム指示ではなく、各リクエストの末尾 に出力言語の指定を置く。モデルは直近のトークンに強く反応します
Few-shot の例を日本語で1つだけ入れる。例文が1つあるだけで、以降の応答言語が固定されます
逆に、コードコメントまで日本語化されると困る場面もあります。その場合は「本文は日本語、コード内のコメントは英語」と両方を明示してください。片方だけ書くと、モデルは全体をどちらかに寄せます。
症状 効いた対処
サマリだけ英語になる リクエスト末尾に出力言語を再指定
応答言語が毎回ぶれる 日本語の Few-shot 例を1つ追加
コードコメントまで日本語化 本文とコードの言語を別々に明示
UI や応答言語の設定そのものの置き場所は「Antigravity 2.0 を日本語UI で快適に使うための設定 」に、モデルの実行環境ごとコンテナに閉じ込めて再現性を確保する手順は「Antigravity DevContainer 構築ガイド 」に整理しています。
明日から始めるための最小ステップ
長くなりましたが、明日から始める方のための最小手順を共有します。
まず Ollama をインストール(brew install ollama)し、Gemma 3 4B あたりの軽量モデルを ollama pull gemma3:4b で取得します。次に ollama serve で起動し、Antigravity 側で前述の設定でカスタムプロバイダを追加します。最後に Antigravity でテキスト生成タスクを 1 回実行して、応答が返ってくることを確認します。
ここまでで小一時間かかります。動いたら、徐々に大きなモデルや複雑なタスクに挑戦してみてください。
ローカル LLM は「クラウド LLM の代替」ではなく「クラウド LLM の補完」と捉えるのが現実的です。両方を使いこなせる Antigravity 環境を持っていると、扱える仕事の幅が確実に広がります。私が機密案件を引き受けられるようになったのは、この組み合わせを整えたおかげです。同じような壁を感じている方の参考になれば幸いです。