OpenZiti / LLM-Gateway について
NetFoundry LLM Gateway は OpenZiti プロジェクトの OpenAI 互換 API プロキシで、OpenAI、Anthropic、その他の互換バックエンドへリクエストをルーティングします。zrok と OpenZiti により、NAT 配下の推論サーバーにポートを開かずに接続できます。複数の LLM バックエンドへゼロトラストでアクセスするチーム向けです。
主な機能
- 01OpenAI 互換 API と Anthropic 形式の変換
- 02モデル名プレフィックスによるルーティング
- 03三層のセマンティックルーティング
- 04重み付きラウンドロビンとヘルスチェックによるフェイルオーバー
- 05zrok 共有によるゼロトラストなバックエンド接続
- 06Prometheus メトリクスと仮想 API キー
活用シーン
- 01Ollama や vLLM のサーバーを単一のゲートウェイ経由で利用
- 02ファイアウォールのポートを開けずに zrok でゲートウェイを公開
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
このサーバーのパッケージやリモートエンドポイントはまだ取得できていません。プロジェクトの README の手順に従ってください。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
llm-gateway
OpenAI、Anthropic、および OpenAI 互換バックエンド(Ollama、vLLM、llama-server、SGLang など)にリクエストをルーティングする、OpenAI 互換の API プロキシです。必要に応じて、zrok 経由でゲートウェイを公開し、ゼロトラストアクセスを実現できます。
ほかの LLM ゲートウェイではなく、これを使う理由
多くの LLM プロキシが解決するのは、API の変換です。このゲートウェイは、ネットワークの問題も解決します。NAT の内側にある GPU マシンにゲートウェイを接続し、ポートを開放せずにクライアントへ公開して、適切なモデルにリクエストをルーティングするにはどうすればよいでしょうか。VPN、サービスメッシュ、ルーティングデータベースを後付けする必要はありません。
- zrok(OpenZiti 経由)によるゼロトラストネットワーク — ゲートウェイとバックエンドは、OpenZiti オーバーレイネットワーク上で zrok を使って通信します。ファイアウォールルールやポートフォワーディングなしで、NAT、エアギャップネットワーク、クラウド境界を越えて、ゲートウェイを公開したりバックエンドにアクセスしたりできます。どちらの方向も同じ方法で動作します。
- セマンティックルーティング — 3 層のカスケード(キーワードヒューリスティック、埋め込み類似度、LLM 分類器)により、クライアントが
modelフィールドを省略した場合に最適なモデルを自動で選択します。手動で管理するルーティングテーブルは不要です。 - 複数エンドポイントのロードバランシング — 推論サーバーのプール全体で、重み付きラウンドロビン、パッシブフェイルオーバーを備えたヘルスチェック、VM のスリープ検出に対応します。Ollama、llama-server、vLLM、SGLang、または
/v1/chat/completionsを公開するあらゆるものと連携します。実際のハードウェアに推論を分散するために設計されています。 - 単一バイナリで、インフラストラクチャ不要 — Go バイナリ 1 つと YAML ファイル 1 つだけです。データベース、メッセージキュー、サイドカーは不要です。
機能
- OpenAI 互換 API:OpenAI クライアントライブラリのドロップイン代替として利用できます
- 複数プロバイダーへのルーティング:モデル名に基づいてリクエストを自動でルーティングします
- セマンティックルーティング:任意で 3 層のカスケード(ヒューリスティック、埋め込み、LLM 分類器)を使い、
modelが省略された場合に最適なモデルを自動で選択します - Anthropic 形式への変換:OpenAI 形式を Anthropic の Messages API との間で透過的に変換します
- ストリーミング対応:すべてのプロバイダーで Server-Sent Events(SSE)ストリーミングに対応します
- 複数エンドポイントのロードバランシング:複数の推論バックエンド間で、ラウンドロビンによる負荷分散と自動フェイルオーバーを行います
- OpenTelemetry メトリクス:リクエスト、レイテンシ、トークン、エンドポイントの健全性に関するメトリクスを Prometheus 向けにエクスポートします
- zrok 連携:zrok のプライベート共有またはパブリック共有を通じてゲートウェイを公開します
- ゼロトラストバックエンド:zrok 共有を通じて任意のプロバイダーに接続します(ポートの公開は不要です)
初めての方へ:ゼロから動作するゲートウェイを構築するまでの手順は、Getting Started ガイドをご覧ください。
インストール
Linux、macOS、Windows 用のビルド済みバイナリは、リリースページから入手できます。
または、Go でインストールします。
go install github.com/openziti/llm-gateway/cmd/llm-gateway@latest
ソースからビルドすることもできます。
git clone https://github.com/openziti/llm-gateway.git
cd llm-gateway
go install ./...
クイックスタート
- 設定ファイルを作成します。
# config.yaml
listen: ":8080"
providers:
open_ai:
api_key: "${OPENAI_API_KEY}"
anthropic:
api_key: "${ANTHROPIC_API_KEY}"
local: # works with any OpenAI-compatible backend
base_url: "http://localhost:11434"
- ゲートウェイを実行します。
export OPENAI_API_KEY="sk-..."
export ANTHROPIC_API_KEY="sk-ant-..."
llm-gateway run config.yaml
- 任意の OpenAI 互換クライアントを使ってリクエストを送信します。
curl http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4o",
"messages": [{"role": "user", "content": "Hello!"}]
}'
プロバイダーへのルーティング
リクエストはモデル名のプレフィックスに基づいてルーティングされます。
| モデルのプレフィックス | プロバイダー |
|---|---|
gpt-* | OpenAI |
o1-* | OpenAI |
o3-* | OpenAI |
claude-* | Anthropic |
| その他すべて | ローカル/セルフホスト |
OpenAI または Anthropic のプレフィックスに一致しないモデルは、すべてローカルプロバイダーにルーティングされます。Ollama、vLLM、llama-server、SGLang など、あらゆる OpenAI 互換バックエンドと連携します。
複数エンドポイントの負荷分散
複数の推論バックエンドにリクエストを分散し、負荷分散と耐障害性を実現します。endpoints が指定されている場合、ゲートウェイはラウンドロビン方式で選択し、自動フェイルオーバーを行います。
providers:
local:
endpoints:
- name: gpu-box-1
base_url: "http://10.0.0.1:11434"
weight: 3
- name: gpu-box-2
base_url: "http://10.0.0.2:11434"
- name: remote
zrok_share_token: "abc123"
health_check:
interval_seconds: 30 # default: 30
timeout_seconds: 5 # default: 5
各エンドポイントでは、直接 HTTP または zrok share を使用できます。省略可能な weight(デフォルト:1)は、エンドポイントが受け取るトラフィックの割合を制御します。weight が 3 のエンドポイントは、weight が 1 のエンドポイントの約 3 倍のリクエストを受け取ります。バックグラウンドの goroutine が設定された間隔で各エンドポイントに ping を送信し、異常なエンドポイントをマークして自動的にスキップします。リクエスト中のネットワークエラーも、即時のパッシブフェイルオーバーを引き起こします。ローカルプロバイダーを使用するすべてのゲートウェイ機能(チャット補完、埋め込み、分類器)で、エンドポイントグループ内にリクエストを分散します。
すべてのエンドポイントで同じソフトウェアを実行する必要はありません。同じプール内で Ollama、vLLM、llama-server、その他の OpenAI 互換サーバーを混在させることができます。負荷分散レイヤーは、URL の背後で何が動作しているかを問いません。
API エンドポイント
POST /v1/chat/completions
OpenAI 互換のチャット補完エンドポイントです。ストリーミング(stream: true)と非ストリーミングの両方のリクエストをサポートします。セマンティックルーティングが有効な場合、model フィールドは省略できます。
GET /v1/models
設定されているすべてのプロバイダーの利用可能なモデルを返します。セマンティックルーティングが有効な場合、自動モデル選択を行う auto 仮想モデルが含まれます。複数エンドポイントモードでは、正常なすべてのエンドポイントから取得したモデルを重複なしで返します。
GET /health
HTTP 200 とともに {"status":"ok"} を返します。死活監視に使用します。
GET /metrics
Prometheus メトリクスエンドポイントです(metrics.enabled: true の場合)。リクエスト数、所要時間のヒストグラム、トークンカウンター、ルーティングの判断、プロバイダーエラー、処理中リクエストのゲージ、エンドポイントのヘルス状態を公開します。
設定
設定の全例
listen: ":8080"
zrok:
share:
enabled: false
mode: private # public or private
token: "" # use existing persistent share (private only)
providers:
open_ai:
api_key: "${OPENAI_API_KEY}"
base_url: "" # optional: override for Azure or compatible APIs
zrok_share_token: "" # optional: connect via zrok share
anthropic:
api_key: "${ANTHROPIC_API_KEY}"
base_url: "" # optional: override base URL
zrok_share_token: "" # optional: connect via zrok share
local:
base_url: "http://localhost:11434"
zrok_share_token: "" # optional: connect via zrok share
# or use multi-endpoint mode for round-robin + failover:
# endpoints:
# - name: gpu-box-1
# base_url: "http://10.0.0.1:11434"
# - name: gpu-box-2
# base_url: "http://10.0.0.2:11434"
# - name: remote
# zrok_share_token: "abc123"
# health_check:
# interval_seconds: 30
# timeout_seconds: 5
metrics:
enabled: false
環境変数
API キーでは、${VAR} 構文を使用した環境変数の展開をサポートしています。
API キー
ゲートウェイは、クライアント認証用の仮想 API キーをサポートしています。キーを生成し、設定に追加します。
llm-gateway genkey
# sk-gw-a1b2c3d4e5f6...
api_keys:
enabled: true
keys:
- name: alice
key: "sk-gw-a1b2c3d4e5f6..."
クライアントは Authorization: Bearer <key> ヘッダーでキーを送信します。/health と /metrics エンドポイントは、引き続き認証なしで利用できます。キーは、グロブパターンを使用して特定のモデルに制限できます。クライアント向けの仕様については docs/current/api-keys.md、再読み込み可能な YAML および HTTP ソースについては docs/current/key-sources.md を参照してください。
セマンティックルーティング
設定すると、ゲートウェイはリクエストの内容に基づいて最適なモデルを自動選択できます。model フィールドは省略可能になり、省略された場合、リクエストは次の 3 層カスケードを通ります。
- ヒューリスティクス — 高速なキーワード/パターンマッチング(例:「翻訳」→高速モデル、ツール使用→ツール対応モデル)
- 埋め込み — Ollama または OpenAI の埋め込みを使用し、ユーザープロンプトとルートの例文とのコサイン類似度を計算
- LLM 分類器 — 埋め込みで判断できない場合に、LLM にリクエストの分類を依頼
各層は個別に有効化または無効化できます。たとえば、埋め込みを使わずに分類器を使用できます。すべての層がスキップされた場合、または結論が出ない場合は、設定されたデフォルトルートが使用されます。セマンティックルーティングが無効または未設定の場合、動作は変わりません。
セマンティックルーティングの設定
routing:
allow_explicit_model: true # clients can still specify a model directly
default_route: general # fallback when no layer matches
heuristics:
enabled: true
rules:
- match:
keywords: ["translate", "translation"]
route: fast
- match:
has_tools: true
route: tools
- match:
system_prompt_contains: "you are a code assistant"
route: coding
- match:
max_tokens_lt: 100
message_length_lt: 200
route: fast
semantic:
enabled: true
provider: local # local or openai
model: nomic-embed-text
threshold: 0.82 # confident match
ambiguous_threshold: 0.65 # escalate to classifier
comparison: centroid # centroid, max, or average
classifier:
enabled: true
provider: local
model: llama3
timeout_ms: 5000
confidence_threshold: 0.7
routes:
- name: coding
model: claude-sonnet-4-20250514
description: "code generation, debugging, and technical tasks"
examples:
- "write a python function to sort a list"
- "debug this segfault in my C code"
- name: creative
model: claude-sonnet-4-20250514
description: "creative writing, storytelling, and artistic content"
examples:
- "write a poem about the ocean"
- "tell me a story about a dragon"
- name: fast
model: llama3
description: "simple tasks, translations, and short responses"
examples:
- "translate hello to French"
- "what is 2+2"
- name: tools
model: gpt-4
description: "tasks requiring tool use and function calling"
examples:
- "search the web for recent news"
- "call the weather API for New York"
- name: general
model: llama3
description: "general conversation and miscellaneous tasks"
examples:
- "what is the capital of France"
- "explain quantum computing"
ヒューリスティックマッチ条件
各ヒューリスティックルールには、1 つ以上の条件を含む match ブロックがあります。1 つのルールに複数の条件が指定されている場合、すべての条件が一致する必要があります(AND ロジック)。keywords 内では、いずれかのキーワードが一致するとルールが適用されます(OR ロジック)。使用できる条件は次のとおりです。
| 条件 | 説明 |
|---|---|
keywords | ユーザーメッセージの内容に対する、大文字と小文字を区別しない単語境界マッチ |
has_tools | リクエストにツール定義が含まれる、または含まれない場合に一致します |
system_prompt_contains | システムメッセージに対する部分文字列マッチ |
max_tokens_lt | max_tokens がしきい値を下回る場合に一致します |
message_length_lt | メッセージ全体の文字数がしきい値を下回る場合に一致します |
exclude | ユーザーメッセージ内に見つかった場合、キーワードマッチを抑制するフレーズのリスト |
モデルを指定せずにリクエストを送信する
セマンティックルーティングが有効な場合、model フィールドを省略できます。
curl http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"messages": [{"role": "user", "content": "Write a Python function to sort a list"}]
}'
チャットクライアントでの使用(Open WebUI など)
Open WebUI などのチャットクライアントでは、モデルリストからモデルを選択する必要があり、常に model フィールドを送信します。セマンティックルーティングが有効な場合、ゲートウェイは自動ルーティングを実行する仮想の auto モデルを公開します。
curl http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "auto",
"messages": [{"role": "user", "content": "Write a Python function to sort a list"}]
}'
クライアントの接続先を http://localhost:8080/v1 に設定し、モデルリストから auto を選択します。ゲートウェイはセマンティックルーティングのカスケードを通じて、各リクエストをルーティングします。
ゲートウェイは、使用した方式、信頼度スコア、レイテンシ、カスケードのトレースとともに、各ルーティング判断をログに記録します。
メトリクス
Prometheus エクスポーターを使用して OpenTelemetry メトリクスを有効にします。
metrics:
enabled: true
有効にすると、ゲートウェイは同じリッスンアドレスの GET /metrics で Prometheus メトリクスを提供します。使用可能なメトリクスは次のとおりです。
| メトリクス | 種類 | 説明 |
|---|---|---|
llm_gateway.requests | カウンター | リクエストの合計(プロバイダー、モデル、ストリーミング別) |
llm_gateway.request.duration | ヒストグラム | リクエストの所要時間(秒)(プロバイダー、モデル別) |
llm_gateway.tokens.prompt | カウンター | プロンプトトークンの合計(プロバイダー、モデル別) |
llm_gateway.tokens.completion | カウンター | 補完トークンの合計(プロバイダー、モデル別) |
llm_gateway.routing.decisions | カウンター | セマンティックルーティングの判断(方式別) |
llm_gateway.provider.errors | カウンター | プロバイダーのエラー(error_type 別) |
llm_gateway.requests.inflight | ゲージ | 現在処理中のリクエスト数 |
llm_gateway.endpoint.healthy | ゲージ | エンドポイントのヘルスステータス |
トレーシング
ルーティング判断のデバッグ用に、リクエスト本文のログ記録を有効にします。
tracing:
enabled: true
max_content_length: 200 # max characters per message in log output
各チャット補完リクエストは、モデル、メッセージ数、ストリーミングフラグ、ツール数、および各メッセージのロールと省略表示された内容とともにログに記録されます。詳細については、docs/current/configuration.md を参照してください。
CLI リファレンス
llm-gateway run <configPath>
Flags:
--address string listen address (overrides config)
--zrok enable zrok sharing (overrides config)
--zrok-mode string zrok share mode: public, private (overrides config)
zrok との統合
ゲートウェイの公開
ファイアウォールのポートを開かずにゲートウェイを公開するには、zrok 共有を有効にします。
zrok:
share:
enabled: true
mode: private
または CLI で設定します。
llm-gateway run config.yaml --zrok --zrok-mode private
ゲートウェイは起動時に共有トークンをログに記録します。クライアントは zrok access を使用して接続します。
永続共有の使用
再起動後も共有トークンを維持するには、zrok CLI で永続共有を作成し、それを指定します。
zrok:
share:
enabled: true
token: "abc123xyz" # your persistent share token
zrok 経由でバックエンドに接続する
直接 URL の代わりに zrok 共有を使用して、任意のプロバイダーに接続できます。
providers:
open_ai:
api_key: "${OPENAI_API_KEY}"
zrok_share_token: "openai-proxy-share-token"
anthropic:
api_key: "${ANTHROPIC_API_KEY}"
zrok_share_token: "anthropic-proxy-share-token"
local:
zrok_share_token: "ollama-share-token"
例
Python OpenAI クライアントでの使用
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8080/v1",
api_key="not-needed" # gateway handles auth
)
# routes to OpenAI
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Hello!"}]
)
# routes to Anthropic (translated automatically)
response = client.chat.completions.create(
model="claude-sonnet-4-20250514",
messages=[{"role": "user", "content": "Hello!"}]
)
# routes to local backend (Ollama, vLLM, etc.)
response = client.chat.completions.create(
model="llama3.2",
messages=[{"role": "user", "content": "Hello!"}]
)
ストリーミング
stream = client.chat.completions.create(
model="claude-sonnet-4-20250514",
messages=[{"role": "user", "content": "Write a haiku"}],
stream=True
)
for chunk in stream:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="")
ドキュメント
- はじめに -- セットアップ手順を順を追って解説します
- 設定 -- 設定と CLI フラグの完全なリファレンスです
- プロバイダー -- プロバイダーの詳細、ストリーミング、エラー処理について説明します
- セマンティックルーティング -- しきい値の調整、比較モード、キャッシュについて説明します
- API キー -- キーごとのモデルとルートの制限について説明します
- キーソース -- 再読み込み可能なキー情報、YAML と HTTP の仕様、古さ、可観測性について説明します
- 複数エンドポイントの負荷分散 -- 重み付けによる負荷分散とフェイルオーバーについて説明します
- メトリクス -- Prometheus の計測項目とクエリ例について説明します
- ストリーミング -- SSE ストリーミングの詳細について説明します
- zrok -- 共有とアクセスのためのオーバーレイネットワークについて説明します
- Agora -- サービス提供と接続のための Agora オーバーレイネットワーク(zrok へのピアトランスポート)について説明します
- ダミーモデル -- テストやデモ用の、OpenAI 互換の偽バックエンドについて説明します
- ダミーキー -- 障害注入機能を備えた、HTTP キーソース仕様を提供するキー API のリファレンスです
ライセンス
Apache 2.0
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。