課金ロジックまでクラウドに読ませてよいのか
Antigravity のエージェントは便利ですが、コードベース全体を読ませる運用に切り替えたとき、ふと手が止まりました。そこには Stripe の課金フロー、アプリ内課金のレシート検証、売上に直結するサーバーロジックが含まれています。これらをまるごとクラウドのエージェントに渡してよいのか——個人開発で App Store と Google Play に課金アプリを出している私自身にとって、これは抽象的なセキュリティ論ではなく、日々の実務判断でした。
かといって、すべてをローカルだけで処理するのは現実的ではありません。ローカル LLM は手元のマシンで完結する安心がある反面、大規模な設計判断やコードベース横断の調査では、クラウドのエージェントの方が圧倒的に速く深い。だから二択ではなく、機密部分はローカル・一般部分はクラウドへ振り分ける設計が要ります。ここでは Ollama と Gemma を Antigravity と併用する前提で、その振り分けを組み立てます。
まず Ollama + Gemma をローカルに用意する
ローカル側は Ollama でモデルを動かします。Gemma 系はコード読解と要約のバランスがよく、個人のマシンでも実用的な速度で動きます。
# Ollama でローカルモデルを用意する
ollama pull gemma:7b # 軽快さ重視。日常のコード読解向け
ollama pull gemma:27b # 精度重視。設計レビューを任せたいとき
# ローカル API として待ち受けているか確認
curl http://localhost:11434/api/generate -d '{
"model": "gemma:7b",
"prompt": "次の関数の役割を1行で要約してください",
"stream": false
}'
7b と 27b を用途で使い分けるのが実用的です。日常のコード理解は 7b で十分に速く、設計判断やレビューのように精度が要る場面だけ 27b に上げます。マシンのメモリと相談しながら、まず 7b を既定にすることをお勧めします。
ファイルの機密度を自動でスコアリングする
振り分けの核は「このファイルはローカルで処理すべきか、クラウドへ出してよいか」を機械的に判定することです。人間の都度判断に任せると、忙しい日に必ず漏れます。私はファイルパスと中身のシグナルから機密度スコアを出しています。
# sensitivity_score.py — ファイルの機密度を採点し処理先を決める
import re
SECRET_PATTERNS = [
r "sk_live_" , r "STRIPE_SECRET" , r "priceId" , r "webhook . * secret" ,
r "receipt . * verif" , r "private [ _- ] ? key" , r " \. env" ,
]
SENSITIVE_PATH = [ "/billing/" , "/payments/" , "/auth/" , "/secrets/" , "config/pricing" ]
def score (path, content):
s = 0
for p in SENSITIVE_PATH :
if p in path:
s += 3
for pat in SECRET_PATTERNS :
if re.search(pat, content, re. IGNORECASE ):
s += 4
return s
def route (path, content):
sc = score(path, content)
# 閾値以上はローカル固定。境界値は自分のリスク許容度で較正する
return "local" if sc >= 3 else "cloud-ok"
print (route( "src/config/pricing.ts" , "export const priceId = 'price_x'" )) # local
print (route( "src/utils/format.ts" , "export const toYen = n => n" )) # cloud-ok
補足すると、機密度の判定はファイルパスだけでなく中身のシグナルも見るのが肝心です。一般的なユーティリティに見えるファイルでも、中に課金キーやレシート検証ロジックが紛れ込んでいることはよくあります。パスと中身の両方を採点に入れておくと、こうした「見た目は安全なのに中身が危険」なファイルを取りこぼしません。実際に私自身、ユーティリティ風の名前のファイルにこっそり書いた検証ロジックを、中身シグナルのおかげでローカルへ寄せられた経験があります。
スコアの閾値は最初から完璧を狙わず、まず保守的(少しでも疑わしければローカル)に振り、運用しながら下げていくのが安全です。本番で踏みたくないのは「機密と気づかずクラウドへ出してしまう」失敗なので、誤ってローカルに寄せる側の過剰さは許容します。deny by default の発想です。
ローカルとクラウドの体感差を測っておく
振り分けを設計するなら、ローカルに寄せたときに自分がどれだけ待たされるかを把握しておくべきです。体感差を測らずにローカルへ寄せると、「安全だけど遅すぎて使わなくなる」という最悪の結末になります。
私の手元の環境で、同じ「単一関数の要約」を投げたときの待ち時間の目安は次のようなものでした。
待ち時間の目安(単一ファイルの要約)
Gemma 7b(ローカル): 数秒。日常使いに支障なし。
Gemma 27b(ローカル): 十数秒。レビュー用途なら許容範囲。
クラウドエージェント: 体感は速いが、ネットワークとキューイング次第でばらつく。
重要なのは、機密ファイルの「要約」や「単一関数の説明」程度ならローカル 7b で十分実用的だ、という点です。ローカルが向かないのは、コードベース横断の大規模調査やマルチファイルのリファクタ。そこはクラウドに任せ、ただし機密ファイルは事前に除外しておく、という棲み分けに落ち着きました。
機密が外へ出ていないことを後から検証する
設計が正しく効いているかは、後から監査できなければ意味がありません。私は「どのファイルをどちらへ送ったか」を必ずログに残し、機密判定されたファイルがクラウド側のログに一度も現れていないことを定期的に確認しています。
# routing_audit.py — 振り分け結果を JST で記録し、漏れを検証可能にする
import json, datetime, pathlib
AUDIT = pathlib.Path.home() / "routing_audit.jsonl"
def log_route (path, destination, sensitivity):
jst = datetime.timezone(datetime.timedelta( hours = 9 ))
rec = {
"ts" : datetime.datetime.now(jst).isoformat(),
"path" : path, "dest" : destination, "score" : sensitivity,
}
with AUDIT .open( "a" ) as f:
f.write(json.dumps(rec) + " \n " )
def verify_no_leak ():
# 機密(score>=3)なのに cloud へ送られた記録がないかを検査
leaks = []
for line in AUDIT .read_text().splitlines():
r = json.loads(line)
if r[ "score" ] >= 3 and r[ "dest" ].startswith( "cloud" ):
leaks.append(r[ "path" ])
return leaks
print ( "leaks:" , verify_no_leak()) # 空であるべき
verify_no_leak() が空でない日は、スコアリングの閾値かパスパターンに穴があったということです。その場合は該当ファイルのパターンを追加し、二度と同じ漏れが起きないようにします。落とし穴として、監査ログの時刻も必ず JST で記録しないと、日付をまたいだ検証で取りこぼしが出ます。本番運用では、この監査ログが「安全だと言い切れる根拠」そのものになります。
二択をやめると、安心して速く動ける
ローカル LLM の話は「クラウドは危険だから全部ローカルへ」という極論になりがちですが、実際に効くのは振り分けの設計です。機密度をファイル単位で採点し、危ういものだけローカル 7b/27b に固定し、残りはクラウドエージェントの速さを享受する。そして送り先を監査ログで後から検証できるようにしておく。
この設計にしてから、課金ロジックを前にして手が止まることがなくなりました。安全側の線を機械が引いてくれるので、人間は安心して速い方を選べます。同じように、クラウドのエージェントに機密コードを渡すことへ迷いがある方の参考になれば幸いです。