前編では、Ollama で Gemma 4 をローカル起動し、対話で動かすところまでを扱いました。後編は、その手元のモデルを実際の作業に組み込む段に踏み込みます。対話ウィンドウに手で打ち込むだけでは、便利さは半分しか引き出せません。スクリプトから呼べるようにして初めて、繰り返す作業を任せられるようになります。
私自身、個人開発で、App Store や Google Play に出すアプリのストア文言やブログの下書きを大量に試す場面があり、その試行をローカルのGemma 4に肩代わりさせています。ここでは、API の叩き方、速度の詰め方、そしてクラウドへの安全な逃がし方を、実装を交えて整理します。
Ollama はローカルにREST APIを立てている
意外と知られていませんが、Ollama は起動すると裏でローカルにHTTPサーバーを立てています。既定では localhost:11434 で待ち受けていて、ここに投げれば対話ウィンドウを開かずにモデルを呼べます。まずは生成エンドポイントを叩いてみるのが分かりやすいです。
curl http://localhost:11434/api/generate -d '{
"model": "gemma4:e2b",
"prompt": "アプリのレビュー返信を3パターン、丁寧な敬体で出してください",
"stream": false
}'
stream を false にすると、生成が終わってから結果をまとめて受け取れます。スクリプトに組み込むときは、この一回呼び出しの形がいちばん扱いやすいです。逆に、長文を少しずつ表示したい用途では stream を true にして、届いた断片を順に処理します。
Python から呼んで繰り返し作業に組み込む
CLIから直接叩いてもよいのですが、繰り返し使うならスクリプトにまとめておくと楽です。標準ライブラリだけで書けるのも、依存を増やしたくない個人開発と相性が良い点でした。
import json, urllib.request
def ask_local (prompt: str , model: str = "gemma4:e2b" ) -> str :
payload = json.dumps({ "model" : model, "prompt" : prompt, "stream" : False }).encode()
req = urllib.request.Request(
"http://localhost:11434/api/generate" ,
data = payload, headers = { "Content-Type" : "application/json" },
)
with urllib.request.urlopen(req, timeout = 120 ) as res:
return json.loads(res.read())[ "response" ]
if __name__ == "__main__" :
print (ask_local( "このメモを箇条書き3点に要約してください: ..." ))
このくらいの薄いラッパーがあれば、定型の指示を関数として何度でも呼べます。私はストア説明文の言い回し出しや、メモの要約をこの形で回していて、手で打ち直す手間が一段消えました。timeout を必ず付けておくのが地味に大事で、モデルが詰まったときに処理ごと固まるのを防げます。
応答速度を体感で上げる
ローカルモデルでいちばん気になるのが、最初の一回の遅さです。モデルがメモリに読み込まれるまでの待ちが、初回だけ長く感じられます。ここは keep_alive の指定で大きく改善します。
curl http://localhost:11434/api/generate -d '{
"model": "gemma4:e2b",
"prompt": "ping",
"keep_alive": "30m"
}'
keep_alive を長めに指定すると、モデルがメモリに常駐し続け、二回目以降の呼び出しが速くなります。連続で何度も投げる作業の前に、一度ウォームアップの呼び出しを入れておくのがお勧めです。それでも遅いと感じる場合は、より小さいサイズのモデルに落とすか、プロンプトを短く保つのが効きます。出力を短く制限したいときは options の num_predict で上限トークン数を抑えると、待ち時間が読みやすくなりました。
ローカルとクラウドを自動で切り替える
ローカルのGemma 4は軽い用途には十分ですが、込み入った指示や最新情報が要る作業では力不足を感じることがあります。そこで、ローカルでまず試し、失敗や品質不足のときだけクラウドのGemini APIへ逃がす二段構えにしておくと、費用を抑えつつ取りこぼしを防げます。
def ask (prompt: str ) -> str :
try :
out = ask_local(prompt)
if out and len (out.strip()) > 20 : # 明らかに空・短すぎる応答は不採用
return out
except Exception :
pass
# ローカルが不調・不十分ならクラウドへフォールバック
return ask_gemini_cloud(prompt) # 別途、Gemini API を呼ぶ実装
ここでの判断軸は、ローカルの応答が「使えるか」を簡単な条件で見て、駄目ならクラウドに上げる、という単純なものです。私の運用では、まず手元で雑に何案か出し、最終仕上げだけクラウドに任せる流れにしています。この切り替えを自動化しておくと、判断のたびに手を止めずに済みます。本番のスクリプトに組み込むなら、クラウド側の失敗も想定して、最終的に必ず何かを返す経路を用意しておくのが安全です。
つまずきやすい3つの落とし穴
実際に組み込む中で、私がハマった注意点を3つ挙げておきます。
初回ダウンロードの待ちを失敗と勘違いする
モデルの取得は回線次第で数分かかります。進捗が伸びている間は正常なので、success の表示まで待つのが確実です。スクリプトから初めて呼ぶときは、未取得モデルだと最初の一回が極端に長くなる点に注意してください。事前に ollama run で一度落としておくのが回避策です。
メモリ不足で応答が極端に遅くなる
上位サイズのモデルを積みすぎると、メモリが足りずスワップが起きて、応答が一気に重くなります。対処は、常駐させるモデルを絞ることと、用途に対して過剰なサイズを避けることです。小型版で足りる作業に大型を使わない、という割り切りが効きました。
出力の体裁が安定しない
軽量モデルは、指示した出力形式から外れることがあります。箇条書きやJSONで欲しいときは、プロンプトで形式を具体的に示し、受け取った側でも軽く検証するのを推奨します。整形を完全にモデル任せにせず、スクリプト側で最低限の後処理を入れておくと、後段が安定します。
どこまでローカルに任せるか
最後に、線引きの話です。手元のGemma 4に向くのは、試行回数を増やしたい下書き、外に出したくないメモの相談、ネットが不安定な場所での軽作業です。逆に、最新情報の反映や長大なコンテキストを要する作業は、無理にローカルで粘らずクラウドへ上げた方が結果が安定します。
私の場合、この住み分けを自動化してから、APIコストを抑えつつ試行の手数を増やせるようになりました。手元で気兼ねなく案を量産し、ここぞという仕上げだけクラウドに託す。個人開発で道具を長く使ううえでは、この二段構えがいちばん現実的だと感じています。まずは前編のスクリプトに、今日紹介した一回呼び出しのラッパーを1つ足すところから始めてみてください。