1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Gemini APIがダメならOpenClawのローカルAPIを使えばいいじゃない

1
Posted at

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 を環境変数だけで切り替えやすくなります。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?