セルフホストモデル

Takuto Core は、セルフホストの OpenAI 互換モデルサーバー — LM Studio、Ollama、vLLM など — を OpenCode プロバイダー経由で動かせます。これにより、ソースコードとプロンプトを完全に あなたが管理するインフラ上にとどめられます。このページでは、セットアップと、LM Studio を Docker Desktop と組み合わせて配線する際に私たちが踏んだ失敗モードを扱うので、あなたが 同じことを再発見せずに済むようにします。

以下のすべての設定は、ダッシュボードの 設定 → AI Settings → OpenCode から編集できます。 config.toml を手編集する必要はありません。

基本セットアップ

  1. 設定 → AI Settings でプロバイダーを OpenCode に設定します。

  2. Model フィールドを設定します。OpenCode の -m フラグは <providerId>/<modelId> を 期待し、Takuto Core が生成する設定は常にプロバイダーを self_hosted と名付けるので、 値は次のような形になります:

    self_hosted/<model-id-as-reported-by-the-server>
    サーバーが報告するモデル idTakuto に設定する値
    qwen/qwen2.5-vl-7bself_hosted/qwen/qwen2.5-vl-7b
    lmstudio-community/Llama-3.1-8B-Instruct-GGUFself_hosted/lmstudio-community/Llama-3.1-8B-Instruct-GGUF
  3. Base URL を、末尾の /v1 を含む OpenAI 互換エンドポイントに設定します:

    http://host.docker.internal:1234/v1
  4. サーバーが本物の API キーを必要としない場合(LM Studio、Ollama は通常不要)、 Allow shared default token にチェックを入れて、Takuto Core がプレースホルダーの bearer を注入するようにします。

  5. Context limit / Output limit をモデルに合わせて設定します — ローカルエンドポイントは これらを報告できないので、OpenCode はあなたが指定した値に頼ります。

モデルサーバー側

サーバーを container から到達可能にします:

  • LM Studio: Developer → Local Server → “Serve on Local Network” をオンにします。 これがないと LM Studio は 127.0.0.1 にしかバインドせず、どの container も到達できません。
  • macOS のファイアウォール: オフにするか、モデルサーバーを明示的に許可します。

ループバックだけでなく、すべてのインターフェースにバインドされていることを確認します:

lsof -nP -iTCP -sTCP:LISTEN | grep 1234
# Want: *:1234 (LISTEN)   — bound to all interfaces
# If it shows 127.0.0.1:1234, "Serve on Local Network" is OFF.

ブリッジサイドカー(gVisor 回避策)

これが必要なのは、モデルサーバーがホスト Mac 上で動き かつ Docker Desktop が gVisor ネットワークスタック(4.34+ のデフォルト)を使っている場合だけです。 クラウドプロバイダーには 不要で、モデルサーバーが Takuto Core の compose ネットワーク内の container として動く場合も 不要です — その場合は Base URL をサービス名(例: http://lm-studio:1234/v1)に向けて、 ブリッジはスキップしてください。

gVisor では、container から Mac の LAN 上のプライベート IP への — host.docker.internal (container 内では 192.168.65.254 に解決されます)を含む — トラフィックが、パブリック IP は 問題なく動くのに、黙ってタイムアウトすることがあります。ネットワークの種類を確認します:

ps ax | grep com.docker.virtualization | grep -o 'networkType [a-z]*'
# networkType gvisor   ← affected

gVisor の問題であることを確認する

サーバーが *:1234 にバインドし、ファイアウォールがオフで、container からパブリック インターネットには到達できるのに、container 内から host.docker.internal:1234 と Mac の LAN IP の両方がタイムアウトする — これが gVisor スタックです。

# Public internet works from a container:
docker exec <takuto-container> sh -c 'wget -q -O - -T 3 https://1.1.1.1 | head -3'

# host.docker.internal resolves but connections time out:
docker exec <takuto-container> sh -c 'curl -v -m 3 http://host.docker.internal:1234/v1/models 2>&1 | tail -5'
# * Connection timed out after 3001 milliseconds

解決策: lm-bridge socat サイドカー

Takuto Core は、デフォルトの bridge ネットワーク(gVisor 下でもホストに正しく到達する)と compose ネットワーク(DinD でネストされたワーカーがルーティングできるように)の 両方 に アタッチする、小さな socat container を同梱しています。フラグ 1 つで起動できます。なお、以下の make ターゲットは Takuto Core エンジンのリポジトリが用意する便利ラッパーです (自分で container を構築する方法)。内部的には LM_BRIDGE=1docker-compose.lm-bridge.yml を Compose スタックにマージしているだけです:

make start BACKEND=postgres LM_BRIDGE=1

これにより、固定 IP 172.20.0.250maestro-lm-bridge が起動し、 TCP/1234 → host.docker.internal:${LM_HOST_PORT:-1234} をフォワードします。その後、 AI Settings の Base URL を次のように設定します:

http://172.20.0.250:1234/v1

デフォルト以外のポート(たとえば Ollama の 11434)の場合:

LM_HOST_PORT=11434 make start BACKEND=postgres LM_BRIDGE=1

ブリッジが起動したらスモークテスト

docker exec maestro-core-dind-1 docker run --rm --entrypoint /bin/bash \
  maestro:latest -c \
  'exec 3<>/dev/tcp/172.20.0.250/1234;
   printf "GET /v1/models HTTP/1.0\r\nHost: x\r\n\r\n" >&3;
   timeout 5 cat <&3 | head -5'

200 OK に続いて JSON が返れば、ワーカーの経路は健全です。

OpenCode error: unknown error のチェックリスト

ダッシュボードに謎めいた OpenCode error: unknown error が表示されたら、これらを順に 確認してください — それぞれが実在する失敗モードです:

  1. Model フィールドが self_hosted/ で始まっている。
  2. Allow shared default token にチェックが入っている(ユーザーごとの bearer を保存した 場合を除く)。オフで bearer も保存されていないと、opencode.json がワーカーにマウントされず、 OpenCode は初回起動時のデータベースマイグレーション直後に終了します。
  3. Base URL/v1 で終わっている。
  4. モデルサーバーで “Serve on Local Network” がオンになっている(127.0.0.1:<port> ではなく *:<port>)。
  5. Docker Desktop のネットワークが健全である — スモークテストを実行し、タイムアウトするなら 上記の LM_BRIDGE サイドカーを使ってください。

5 つすべてが緑なのに実行がまだ失敗する場合は、container 内で opencode run--print-logs --log-level WARN 付きで手動実行し、隠れた「unknown error」の代わりに OpenCode 自身の stderr を確認してください。