AI API で使う VPN を選ぶときの判断基準は「どのノードが一番速いか」ではなく、次の3点です。出口 IP が固定されているか、同時接続が安定しているか、失敗したときにきれいに再試行できるか。Web チャットなら一瞬の遅延は数秒のスピナーで済みますが、スクリプトでは1回のタイムアウトがバッチ全体の再実行につながります。しかも再試行のつもりでノードを切り替えると出口 IP が変わり、相手側の不正検知に引っかかることがあります。以下ではこの3点を順に解説し、そのまま使える設定例も紹介します。
Web チャットと API 呼び出しでは要件が異なる
Web 画面でモデルと対話する場合、クライアントは通常1〜2本の長いコネクションを維持し、切断されればフロントエンドが自動で再接続します。数百ミリ秒の揺らぎを人間が感じ取ることはほとんどありません。スクリプトからの呼び出しは別物です。リクエストごとに名前解決、TCP ハンドシェイク、TLS ハンドシェイクをやり直し、応答を受け取った時点でコネクションが即座に破棄されることもあります。バッチ処理では数十〜数百のリクエストが同時に発生し、1つでも接続に失敗すれば、画面上のスピナーではなく例外としてコードに返ってきます。
| 比較項目 | Web チャット | コードからの API 呼び出し |
|---|---|---|
| 接続方式 | 少数の長いコネクション、SSE / WebSocket によるストリーミング応答 | 大量の短いコネクション、接続の繰り返し |
| 失敗の検知 | 画面はスピナー表示、フロントエンドが自動再接続 | 例外やタイムアウトとして返り、結果は再試行ロジック次第 |
| 出口への要件 | 接続できて維持できればよい | 出口 IP の安定が求められることが多く、許可リスト登録や調査がしやすい |
| 重視する指標 | 初回バイトまでの時間 | 接続成功率とレイテンシの揺らぎ |
| 典型的な用途 | 一問一答の対話 | バッチ生成、ベクトル化、定期実行ジョブ |
この表が説明するのは、よくある疑問です。同じマシンでブラウザは開けるのに、スクリプトは頻繁にタイムアウトする。ブラウザはすでに確立済みの長いコネクションを使いますが、スクリプトは毎回接続をやり直すため、回線の揺らぎに晒される度合いがはるかに大きくなります。つまり「ブラウザで開ける」ことは、その回線が API に向いている証拠にはなりません。
3種類の回線の選び方:専用線・中継・直結
回線を経路でざっくり分けると、よくあるのは IEPL 専用線・中継・直結の3種類です。名前はあくまでラベルで、挙動を決めるのは経路そのものです。データがどこから国外へ出るのか、途中で何ホップ経由するのか、一般トラフィックと同じ出口を奪い合うのかどうか。
| 回線タイプ | 経路の特徴 | 挙動 | 向いている API 用途 |
|---|---|---|---|
| IEPL 専用線 | 国際専用線で直結し、公衆網の出口を経由しない | レイテンシが安定し、揺らぎが小さい | ストリーミング出力、対話型チャット、長いコネクションのタスク |
| 中継 | まず中継ノードに接続し、そこから国外へ出る | 品質は中継ノード次第で、変動は中程度 | 再試行可能なバッチ処理、オフラインジョブ |
| 直結 | ローカル回線から直接国外へ出る | ローカル回線の影響を強く受け、夜のピーク時間帯に顕著 | 予備回線、低頻度の呼び出し |
振り分けの考え方は「ドメインごとに回線を割り当てる」です。api.openai.com や api.anthropic.com といった API ドメインは専用線に固定し、依存パッケージの取得やミラー同期のような大容量トラフィックは直結に任せます。2つの回線は互いにバックアップとし、メイン回線が連続して失敗したときだけ切り替えます。VPNEM の回線は地域別に整理されており、110+ の国・地域、210+ の回線をカバーしています。ノードを選ぶときは IEPL と表示された入口を優先し、実測の揺らぎを見て中継への降格を判断してください。
- 110+対応国・地域
- 210+提供中の回線
- 60日間理由不問の返金期間
- 台数無制限同時接続デバイス
結論:対話型リクエストとバッチ処理は経路を分けます。ストリーミング対話や揺らぎを抑えたい呼び出しは IEPL 専用線へ。オフラインのバッチ処理は中継か直結へ。さらに予備回線を1本用意し、メイン回線が連続して失敗したときだけ引き継がせます。再試行のたびに出口が変わるのを避けるためです。
出口の固定・同時接続・タイムアウトと再試行
出口 IP の固定は「最速」より重要
相手のサーバーから見えるのはあなたの出口 IP です。同じアカウントが数分のうちに複数の出口からリクエストを送ると、軽ければ追加認証を求められ、重ければレート制限を受けます。さらにログ上の送信元が食い違い、問題が回線側なのかコード側なのか判断できなくなります。やることは単純です。クライアントの「遅延が最も低いノードを自動選択」はオフにし、ノードを手動で固定します。チームで1つのサブスクリプションを共有する場合は、同じ出口を使うよう取り決めるか、プロジェクトごとにアカウントを分けてください。
同時接続:帯域よりコネクションプールが鍵
リクエストごとに TLS 接続を新規作成すると、ハンドシェイクのコストがリクエスト数だけ積み上がります。同時実行数が増えると、ボトルネックは帯域ではなく接続確立に現れがちです。keep-alive を有効にし、コネクションプールの上限を同時実行数の1.2〜2倍に設定し、クライアントで HTTP/2 の多重化を有効にするほうが、「より速い」回線に乗り換えるより効果的です。監視でも、ピーク帯域だけでなく接続成功率とハンドシェイク時間を見るようにします。
タイムアウトと再試行:2種類のタイムアウトを分けて設定する
接続タイムアウトと読み取りタイムアウトは別々に設定します。接続タイムアウトは短め(数秒)にして、回線が通らないときは早く失敗させ、残り時間を再試行に回します。読み取りタイムアウトは長めにします。ストリーミング応答は数十秒にわたって出力が続くのが普通だからです。再試行は冪等なリクエストと 5xx、タイムアウトにだけ効かせます。4xx を再試行してもクォータを無駄にするだけです。バックオフにはランダムな揺らぎを入れましょう。そうしないと、多数のワーカーが同じ秒に一斉に再試行し、復旧しかけた回線を再び飽和させてしまいます。
# 現在のターミナルセッションだけに適用し、マシン全体の環境を汚さない
export HTTPS_PROXY="http://127.0.0.1:7890"
export HTTP_PROXY="http://127.0.0.1:7890"
# ローカルと社内ネットワークのアドレスはプロキシを経由させない
export NO_PROXY="localhost,127.0.0.1,::1,10.0.0.0/8,.internal.example.com"
HTTPS_PROXY のような環境変数は curl、Python の requests、Go の標準ライブラリなどがそのまま読み取ります。Node は既定では読み取らないため、undici に ProxyAgent を明示的に設定する必要があります。比較的新しい Node では NODE_USE_ENV_PROXY=1 で実験的な環境変数プロキシ対応を有効にできます。
import httpx
from openai import OpenAI
# コネクションプール:上限 32、常時保持 16。接続タイムアウトと読み取りタイムアウトは別々に
http_client = httpx.Client(
timeout=httpx.Timeout(30.0, connect=8.0),
limits=httpx.Limits(max_connections=32, max_keepalive_connections=16),
)
client = OpenAI(http_client=http_client, max_retries=3)
この設定は上の3点にそのまま対応しています。コネクションプールは同時に張る接続数を制限し、connect=8.0 は回線が通らないときに素早く失敗させ、max_retries=3 は再試行を自前のループではなく SDK に任せます。SDK は既定で接続エラー、408、409、429、5xx に対して指数バックオフ付きの再試行を行うので、自分で実装し直す必要はありません。逆に自前の再試行を重ねると、1回のタイムアウトが3回に膨らんでしまいます。
プロトコルとクライアント:Shadowsocks から Hysteria2 までの使い分け
サブスクリプションのリンクには通常、複数のノードがまとめて含まれます。プロトコルが異なれば、クライアント上での挙動も変わります。以下はよくある種類の位置づけです。選ぶときの原則は1つ、安定して接続できるプロトコルを、理論上より速いプロトコルより優先することです。
| プロトコル | 特徴 | 向いているケース |
|---|---|---|
| Shadowsocks | 軽量、AEAD 暗号化、対応クライアントが豊富 | CPU に余裕がないマシン、設定項目は少ないほどよい場合 |
| VMess | UUID が必要で、複数のトランスポート層と組み合わせ可能 | 既存の設定から移行する場合 |
| Trojan | 標準 TLS を使い、トラフィックの形が HTTPS に近い | ネットワーク環境が TLS に寛容なとき |
| VLESS | それ自体は暗号化せず、TLS / REALITY に依存 | 暗号化のオーバーヘッドを1層分減らしたい場合 |
| Hysteria2 | QUIC / UDP ベースで、輻輳制御が積極的 | パケットロスが多く、揺らぎが大きい回線 |
| TUIC | QUIC ベースで多重化に対応 | ネットワークが頻繁に切り替わるモバイル環境 |
インポートの流れはどのクライアントでもほぼ同じです。サブスクリプションのリンクをコピーし、クライアントで「クリップボードからインポート」を選ぶかサブスクリプション設定に貼り付け、最後に「サブスクリプションを更新」を手動で1回クリックします。ノード一覧がクライアントに表示されて初めてインポート成功です。デスクトップ版は通常 TUN モード(全トラフィックを引き受ける)とシステムプロキシの2通りを用意しています。スクリプトを動かすマシンでは、すべてのトラフィックをトンネルに押し込まず、システムプロキシと振り分けルールを組み合わせるのがおすすめです。iOS ではインポート後に「VPN 構成の追加を許可しますか」というダイアログが出るので、許可をタップしないと有効になりません。
サブスクリプションのリンクはそれ自体が認証情報です。公開リポジトリにコミットしたり、チャットに貼り付けたりしないでください。CI では secret 変数として注入し、ローカルでは少なくとも権限を絞った設定ファイルに置きます。
公開前のセルフチェック:タイムアウト、DNS、振り分けルール
以下のチェックリストは、クライアントとコードを見ながらそのまま確認できます。各項目は実際によくある障害に対応しています。
- ✅ 出口の固定:同じアカウントのリクエストは常に同じ出口から送る。相手側の許可リスト登録にも、自分の調査にも役立ちます。
- ✅ 振り分けルールで必要なドメインだけを国際回線に流す:
api.openai.com、api.anthropic.comなどは専用線、それ以外は直結。 - ✅ DNS とリクエストを同じ回線に通す:クライアントのリモート DNS 解決を有効にし、ドメインが手元に近いノードへ解決されるのを防ぐ。
- ✅ 2種類のタイムアウトを分けて設定する:接続タイムアウトは数秒、読み取りタイムアウトはストリーミング応答の最長時間に合わせて十分に確保する。
- ❌ グローバルプロキシで
NO_PROXYを忘れる:社内ドメインやローカルサービスまで外に出てしまい、「API が突然 502 を返す」という症状になります。 - ❌ 「ノードを自動選択」を使う:再接続のたびに出口 IP が変わり、サーバー側からは複数の送信元に見えます。
- ❌ ping で回線の良し悪しを判断する:ICMP が通っても TLS ハンドシェイクが成功するとは限りません。
time_connectとtime_appconnectを見てください。
回線の検証に追加ツールは不要で、curl 1本で足ります。
curl -x http://127.0.0.1:7890 -sS --connect-timeout 8 -o /dev/null \
-w 'http=%{http_code} connect=%{time_connect}s tls=%{time_appconnect}s total=%{time_total}s\n' \
https://api.openai.com/v1/models
十数回続けて実行し、connect と tls の2列の揺らぎを見ます。絶対値ではありません。揺らぎが非常に小さければ、その回線は対話型リクエストに向いています。ときどき数秒のスパイクが出る場合は、ノードが切り替わっているかローカル回線が揺らいでいると考えられ、そうした回線は再試行可能なバッチ処理向きです。
DNS リークの典型的な症状は「つながるが遅い」「同じドメインなのに速かったり遅かったりする」です。クライアントが TCP だけをプロキシし、DNS をローカルのリゾルバに任せていると、名前解決の結果が出口に近いノードではなく自分に近いノードを指すことがあります。確認方法は、クライアントのログで名前解決がプロキシ経由になっているかを確かめ、振り分けルールを DNS クエリ自体にも適用することです。
開発者向けFAQ
ローカル開発だけでも、回線を専用に用意すべきですか?
はい。開発機での接続の仕組みは本番と変わりません。違うのは同時実行数だけです。ローカルのうちに振り分けルールとタイムアウトを調整しておけば、「ローカルでは問題ないのにサーバーではタイムアウトだらけ」という事態を避けられます。逆に、ローカルでグローバルプロキシ任せに動かしていた設定は、そのままサーバーへ持っていくのはほぼ無理です。
たまにタイムアウトするときは、そのまま再試行すればよいですか?
まずどちらのタイムアウトかを見分けます。接続タイムアウトなら回線かポートの問題で、再試行しても失敗する可能性が高いため、先に回線を変えるべきです。読み取りタイムアウトはストリーミング応答が長すぎることが多く、まず読み取りタイムアウトを延ばし、それから再試行を検討します。2種類のタイムアウトの回数を別々に記録するほうが、ひとつの「失敗率」にまとめるよりはるかに役立ちます。
1つのサブスクリプションで何台まで使えますか?
VPNEM は台数無制限で同時接続でき、開発機、テスト機、CI ランナーで同じサブスクリプションを共有できます。条件は出口の方針を統一することです。すべて同じノードに固定するか、プロジェクトごとにアカウントを分けるか。複数の出口を同じアカウントに混在させないでください。
その回線が本当に専用線かどうかは、どう見分けますか?
名前ではなくデータで判断します。time_connect を連続サンプリングしたとき、専用線のグラフはほぼ水平な直線になるはずです。夜のピーク時間帯に規則的に跳ね上がるなら、直結か品質の一般的な中継に近いと言えます。VPNEM の回線ページには各回線のタイプが表示されているので、実測のグラフと表示を突き合わせて確認できます。
サービスを選んでいる段階なら、次の項目を先に確認しておくとよいでしょう。ログを記録しないか、返金期間はどのくらいか、開通にメールアドレスが必要か、支払い方法が使いやすいか。VPNEM の場合、ログを記録せず、60日間の理由不要の返金に対応し、開通にメールアドレスは不要です。支払いは Alipay、WeChat Pay、USDT に対応しています。回線とプランの詳細は回線ページとプランページで確認できます。
結論:AI API 呼び出しの回線選びは、出口を決める → プロトコルを決める → タイムアウトと再試行を調整する、の順です。出口の固定は不正検知と許可リストの問題を解決し、プロトコルは安定して接続できるかどうかを決め、タイムアウトと再試行は1回の障害がバッチ全体の失敗に膨らむかどうかを決めます。