Gemini APIがダメならOpenClawのローカルAPIを使えばいいじゃない
この記事で言いたいこと
Gemini API は便利ですが、無料枠や低い quota で開発していると、少し多めに LLM を呼ぶだけで rate limit に当たりがちです。
一方、開発環境に OpenClaw が入っているなら、OpenClaw Gateway のローカル API を使って、アプリから OpenAI 互換の chat completions として呼び出せます。
つまり、アプリ側をこういう構成にしておくと便利です。
Application
|
v
LLM provider adapter
|
+-- Gemini API
|
+-- OpenClaw local API
ポイントは、Gemini 固有の呼び出しをアプリ本体に直書きせず、provider を差し替えられる薄い adapter を挟むことです。
OpenClaw のローカル API
OpenClaw Gateway が起動している環境では、ローカルに OpenAI 互換の chat completions endpoint が立ちます。
POST http://127.0.0.1:18789/v1/chat/completions
payload は通常の OpenAI Chat Completions に近い形で送れます。
curl 'http://127.0.0.1:18789/v1/chat/completions' \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer ${OPENCLAW_CHAT_TOKEN}" \
-d '{
"model": "openclaw",
"messages": [
{
"role": "system",
"content": "You are a helpful assistant."
},
{
"role": "user",
"content": "OpenClaw local API の利点を一言で説明して"
}
],
"temperature": 0.4,
"max_tokens": 256
}'
レスポンスは OpenAI 互換の choices[0].message.content から取り出します。
Python から呼ぶ最小例
httpx を使うと、最小限ならこれくらいです。
import os
import httpx
OPENCLAW_CHAT_URL = os.getenv(
"OPENCLAW_CHAT_URL",
"http://127.0.0.1:18789/v1/chat/completions",
)
OPENCLAW_CHAT_TOKEN = os.getenv("OPENCLAW_CHAT_TOKEN", "")
OPENCLAW_CHAT_MODEL = os.getenv("OPENCLAW_CHAT_MODEL", "openclaw")
def generate_with_openclaw(prompt: str) -> str:
headers = {"Content-Type": "application/json"}
if OPENCLAW_CHAT_TOKEN:
headers["Authorization"] = f"Bearer {OPENCLAW_CHAT_TOKEN}"
payload = {
"model": OPENCLAW_CHAT_MODEL,
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": prompt},
],
"temperature": 0.4,
"max_tokens": 512,
}
response = httpx.post(
OPENCLAW_CHAT_URL,
headers=headers,
json=payload,
timeout=120,
)
response.raise_for_status()
body = response.json()
return body["choices"][0]["message"]["content"]
これでアプリ側からは、普通の関数として呼べます。
answer = generate_with_openclaw("Gemini API の rate limit 回避策を短く説明して")
print(answer)
provider adapter にしておく
最初から Gemini と OpenClaw を切り替えられる形にしておくと、quota に当たったときの逃げ道ができます。
例えば、アプリ本体は generate() だけを呼ぶようにします。
from typing import Protocol
class LLMProvider(Protocol):
def generate(self, prompt: str) -> str:
...
OpenClaw provider:
class OpenClawProvider:
def generate(self, prompt: str) -> str:
return generate_with_openclaw(prompt)
Gemini provider:
class GeminiProvider:
def generate(self, prompt: str) -> str:
# Gemini API 呼び出しを書く
...
環境変数で切り替えます。
import os
def create_llm_provider() -> LLMProvider:
provider = os.getenv("LLM_PROVIDER", "openclaw").lower()
if provider == "openclaw":
return OpenClawProvider()
if provider == "gemini":
return GeminiProvider()
raise ValueError(f"Unsupported LLM_PROVIDER: {provider}")
設定例:
LLM_PROVIDER=openclaw
OPENCLAW_CHAT_URL=http://127.0.0.1:18789/v1/chat/completions
OPENCLAW_CHAT_TOKEN=
OPENCLAW_CHAT_MODEL=openclaw
Gemini に戻したい場合:
LLM_PROVIDER=gemini
GEMINI_API_KEY=
GEMINI_MODEL=gemini-2.5-flash-lite
Docker から呼ぶ場合の注意
アプリを Docker コンテナで動かす場合、少し注意が必要です。
コンテナ内の 127.0.0.1 はホストではなくコンテナ自身を指します。OpenClaw Gateway がホスト側の loopback で待ち受けている場合、通常の bridge network からはそのまま届かないことがあります。
Linux 環境なら、backend コンテナだけ network_mode: host にするのが簡単です。
services:
backend:
network_mode: host
environment:
LLM_PROVIDER: openclaw
OPENCLAW_CHAT_URL: http://127.0.0.1:18789/v1/chat/completions
これでコンテナから見た 127.0.0.1:18789 が、ホスト上の OpenClaw Gateway になります。
host.docker.internal を使う方法もありますが、OpenClaw Gateway が loopback にしか bind していない場合は届かないことがあります。そこは環境に合わせて確認が必要です。
token の扱い
token をリポジトリに入れないことだけは守ります。
避けるもの:
-
.env.exampleに実 token を書く -
docker-compose.ymlに実 token を直書きする - README や記事に実 token を貼る
安全寄りの方法:
-
.envにだけOPENCLAW_CHAT_TOKENを書く -
.envは.gitignoreする - OpenClaw の設定ファイルから token を読む
- Docker では設定ファイルを read-only mount する
例えば Compose で設定ファイルを mount するならこうです。
services:
backend:
volumes:
- ${OPENCLAW_CONFIG_HOST_PATH:-${HOME}/.openclaw/openclaw.json}:/openclaw/openclaw.json:ro
environment:
OPENCLAW_CONFIG_PATH: /openclaw/openclaw.json
Python 側では JSON から token を取り出します。
import json
import os
def token_from_openclaw_config() -> str:
config_path = os.getenv("OPENCLAW_CONFIG_PATH", "")
if not config_path:
return ""
with open(config_path, encoding="utf-8") as file:
config = json.load(file)
return str(config.get("gateway", {}).get("auth", {}).get("token", "") or "")
まとめ
Gemini API は手軽ですが、開発中に何度も LLM を呼ぶ用途では quota に当たりやすいです。
OpenClaw を使っている環境なら、ローカルの OpenAI 互換 API を LLM provider として使うことで、Gemini API の回数制限に詰まったときの逃げ道になります。
実装上のポイントはこの3つです。
- LLM 呼び出しを provider adapter に分離する
- OpenClaw Gateway を OpenAI 互換 chat completions として呼ぶ
- token はリポジトリに入れず、
.envや read-only mount した設定ファイルから読む
この形にしておくと、Gemini、OpenClaw、ほかの OpenAI 互換 API を環境変数だけで切り替えやすくなります。