導入:なぜ今、「回線を抜いて」AIを作るのか
企業における生成AIの活用が急速に進む中、データセキュリティとAIの利便性をどう両立させるかは、多くの組織が直面する最大の課題の一つです。
「社内の機密データを外部のAIサービスに入力してはいけない」。このルールは、今や多くの企業で常識となりつつあります。しかし、現場のエンジニアやデータサイエンティストの皆様が直面しているのは、「禁止」のその先にある、「では、どうやって安全かつ効果的にAIを使えばいいのか」という実務的な難題ではないでしょうか。
機密性の高い情報を扱う現場において、一般的に最も切実なのがこの「データの主権」と「AIの利便性」のジレンマです。SaaS型のAIサービスは確かに便利です。しかし、どんなに契約書で「学習に使いません」と明記されていても、データが社外ネットワークに出る時点で、コンプライアンス上のリスクはゼロにはなりません。また、クラウド側のAIモデルは進化のスピードが速く、例えばChatGPTでは2026年2月13日にGPT-4oやGPT-4.1などの旧モデルが廃止され、より高度な推論能力を持つGPT-5.2(InstantおよびThinking)へと移行が進むなど、プラットフォーム側の仕様変更に依存せざるを得ないという運用上の課題も存在します。
そこで、システム全体を俯瞰した際の最適解として挙げられるのが、「完全オフライン(エアギャップ)環境でのローカルLLM構築」です。
これは単なる懐古的なオンプレミス回帰ではありません。最新のオープンソースモデルの進化は目覚ましく、例えば1Bから405Bまでの幅広いパラメータサイズを展開するLlama 3.3や、長文脈対応が進んだMistral Small 3.2などは、特定のタスクにおいてかつてのGPT-4クラスに肉薄、あるいは凌駕する性能を持っています。これらを自社のサーバー、あるいはプライベートクラウド内の完全に閉じたネットワークで動かすことで、情報漏洩のリスクを物理的・技術的に「ゼロ」にすることが可能です。外部の突然の仕様変更や機能廃止に振り回されることなく、自社のペースでAI環境を最適化できる点も、長期的な運用を見据えた際の大きなメリットと言えます。
本記事では、SaaSツールやブラックボックスなAPIに頼らず、Dockerとオープンソースソフトウェア(OSS)を組み合わせて、自社で完全にコントロール可能なAIインフラを構築する手順を解説します。インフラエンジニアやMLOps担当の皆様が、明日からすぐに検証環境を立ち上げられるよう、具体的なコードや設定ファイルを交えて進めていきます。
インターネットへのLANケーブルを抜く準備をしましょう(比喩的な意味でも、物理的な意味でも)。自社のデータは自社の手で守り、そして業務プロセス改善のために最大限に活用していくことが重要です。
1. アーキテクチャ設計:完全オフライン環境の要件定義
まず、手を動かす前にシステム全体を俯瞰し、設計図を描きます。ここでの要件定義の甘さは、後のパフォーマンス不足や運用負荷に直結するため、慎重に進める必要があります。
外部通信ゼロを実現するネットワーク構成
目指すべきは「エアギャップ(Air-gapped)」に近い環境です。これは、インターネットから物理的、あるいは論理的に完全に隔離されたネットワークを指します。企業内ネットワークにおいて、これを実現するためには以下の構成が基本となります。
- 推論サーバー層: LLMが稼働するGPUサーバー。外部へのOutbound通信をファイアウォールで全遮断します。
- データストア層: RAG用のベクトルデータベースや、チャットログを保存するRDB。ここも外部通信は不要です。
- アプリケーション層: ユーザーインターフェース(WebUI)やAPIゲートウェイ。社内LANからのInboundのみを許可します。
この構成において最大の課題となるのが、「モデルのダウンロード」と「ライブラリのインストール」です。通常、pip install や huggingface-cli download はインターネット接続を前提としています。完全オフライン環境では、これらを「踏み台サーバー」で一度取得し、セキュリティチェックを経た上で、閉域網へ転送する運用フロー(Sneakernet的なアプローチ)を設計に組み込む必要があります。
推論エンジン(vLLM vs Ollama vs TGI)の技術選定基準
ローカルでLLMを動かすためのランタイム(推論エンジン)は多数存在しますが、2025年現在の企業ユースにおいて、実務的な観点から推奨されるのは vLLM です。
なぜvLLMが適しているのか、比較してみましょう。
- Ollama: 手軽で個人利用には適していますが、Go言語ベースのラッパーであり、並列処理や高負荷時のスループットにおいて、Pythonベースの高度なバックエンドに劣る場合があります。また、エンタープライズ向けの微細な設定(GPUメモリの厳密な管理など)が隠蔽されがちです。
- Text Generation Inference (TGI): Hugging Face社製で優秀ですが、ライセンスが商用利用において一部制約(Hugging Faceのサービスと競合する場合など)を持つため、導入時に注意が必要です。
- vLLM: カリフォルニア大学バークレー校発のOSSで、PagedAttention というメモリ管理技術により、スループット(単位時間あたりの処理数)が圧倒的に高いのが特徴です。Apache 2.0ライセンスで商用利用しやすく、OpenAI互換のAPIエンドポイントを提供してくれるため、既存のアプリケーションからの移行もスムーズに行えます。
企業インフラとして「安定して」「大量のリクエストを」「高速に」処理するためには、vLLMが現状の最適解と言えます。
GPUリソースのサイジング:VRAM計算と量子化のトレードオフ
「どのGPUを選定すべきか」という問いへの回答は、「動かしたいモデルのパラメータ数」と「量子化(Quantization)レベル」によって決まります。
一般的に、モデルのパラメータ(重み)をFP16(16ビット浮動小数点)でロードする場合、パラメータ数 × 2バイトのVRAMが必要です。さらに、推論時のKVキャッシュ(コンテキストを保持するためのメモリ)用にプラス20〜30%の余裕を見る必要があります。
- Llama-3-8B (FP16): 約16GB VRAM → NVIDIA A10G (24GB) や RTX 4090 (24GB) 1枚で動作。
- Llama-3-70B (FP16): 約140GB VRAM → A100 (80GB) × 2枚、または A10G × 8枚が必要。
しかし、オンプレミスでA100を複数枚用意するのはコスト的に厳しいケースも多いでしょう。そこで重要になるのが量子化技術(AWQやGPTQ)です。モデルを4ビット(INT4)に圧縮することで、メモリ使用量を約1/3〜1/4に削減できます。
- Llama-3-70B (INT4 - AWQ): 約40GB VRAM → A6000 (48GB) 1枚、または RTX 6000 Ada 1枚で動作可能。
社内ドキュメント検索(RAG)用途であれば、70Bクラスのモデルを使うことで回答精度が飛躍的に向上します。コストと精度のバランスを考慮すると、「70Bモデルの4bit量子化版を、48GB VRAM搭載のワークステーションで動かす」のが、2025年のオンプレミスAIにおける実用的な選択肢と言えるでしょう。
2. インフラ構築:Dockerコンテナによる推論サーバーの立ち上げ
設計が固まったところで、実際の構築手順に入ります。環境依存を排除し、再現性を担保するためにDockerを使用します。ホストOSはUbuntu 22.04 LTS以降を想定しています。
NVIDIA Container Toolkitのセットアップ
まず、DockerコンテナからGPUを認識させるために、NVIDIA Container Toolkitを導入します。これは必須の手順となります。
# リポジトリの設定とGPGキーの追加
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
&& curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
# インストール
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit
# Dockerの設定を更新して再起動
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
動作確認として、docker run --rm --gpus all nvidia/cuda:12.1.0-base-ubuntu22.04 nvidia-smi を実行し、GPU情報が表示されれば準備完了です。
モデルウェイトのオフラインロード手順
完全オフライン環境では、コンテナ起動時にHugging Faceへモデルを取得しに行くことができません。インターネットに接続できる環境でモデルを事前にダウンロードし、それをサーバーへ転送します。
# 【インターネット接続環境での作業】
# huggingface-cliを使用してモデルをローカルディレクトリにダウンロード
pip install huggingface_hub
huggingface-cli download meta-llama/Meta-Llama-3-8B-Instruct --local-dir ./models/llama-3-8b --local-dir-use-symlinks False
この ./models/llama-3-8b フォルダを、USBメモリやセキュアなファイル転送経由で、オフラインサーバーの所定のディレクトリ(例: /opt/ai/models/llama-3-8b)に配置します。
vLLMを用いたOpenAI互換APIエンドポイントの構築
推論サーバーを立ち上げます。docker-compose.yml を作成し、vLLMをサービスとして定義します。
version: '3.8'
services:
vllm:
image: vllm/vllm-openai:latest
container_name: local-llm-server
runtime: nvidia
environment:
- CUDA_VISIBLE_DEVICES=0
# オフラインモードを強制
- HF_DATASETS_OFFLINE=1
- TRANSFORMERS_OFFLINE=1
volumes:
# ホスト側のモデルディレクトリをコンテナにマウント
- /opt/ai/models:/models
# キャッシュディレクトリのマウント(念のため)
- ~/.cache/huggingface:/root/.cache/huggingface
ports:
- "8000:8000"
ipc: host
command: >
--model /models/llama-3-8b
--served-model-name llama3
--dtype auto
--api-key internal-secret-key
--max-model-len 4096
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
ポイント解説:
volumes: ダウンロード済みのモデルディレクトリをコンテナ内の/modelsにマウントしています。command:--modelでマウントしたパスを指定します。--api-keyを設定することで、簡易的な認証をかけています(内部ネットワークとはいえ、認証なしでの運用はセキュリティリスクを伴います)。ipc: host: PyTorchの共有メモリ制限を回避するために重要な設定です。
これで docker-compose up -d を実行すれば、http://localhost:8000/v1 でOpenAI互換のAPIが立ち上がります。既存のOpenAI用ライブラリ(Pythonの openai パッケージなど)の base_url を書き換えるだけで利用可能になります。
3. アプリケーション実装:社内文書を検索するRAGパイプライン
LLMが稼働しただけでは、単なるチャットボットに過ぎません。業務プロセス改善に直結する価値を生むのは、社内の膨大なドキュメント(PDF、Word、Wikiなど)を検索し、回答の根拠とする RAG(Retrieval-Augmented Generation) の仕組みです。
外部APIを一切叩かないEmbeddingモデルの選定
RAGの要となるのは、テキストをベクトル化する「Embeddingモデル」です。OpenAIの text-embedding-3 などが広く知られていますが、これを使用するとデータが外部に送信されてしまいます。ここでは、ローカルで動作し、かつ日本語性能が高いモデルを選定します。
実務において推奨されるのは intfloat/multilingual-e5-large または BAAI/bge-m3 です。これらもLLMと同様に、Hugging Faceから事前にダウンロードしておきます。
QdrantによるローカルベクトルDB構築
ベクトルデータベースも、SaaS(Pinecone等)ではなく、Dockerで稼働するOSSを採用します。Qdrant はRust製で高速に動作し、かつAPIが扱いやすいため推奨されます。
docker-compose.yml にQdrantを追加します。
qdrant:
image: qdrant/qdrant:latest
container_name: local-vector-db
ports:
- "6333:6333"
volumes:
- ./qdrant_storage:/qdrant/storage
LangChainを用いた検索・回答生成フローの実装
Pythonでの実装例を確認してみましょう。LangChainを使用して、ローカルのvLLMとQdrant、そしてローカルのEmbeddingモデルを連携させます。
from langchain_community.llms import VLLMOpenAI
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.vectorstores import Qdrant
from langchain.chains import RetrievalQA
from qdrant_client import QdrantClient
# 1. ローカルLLMの設定 (vLLMのエンドポイントを指定)
llm = VLLMOpenAI(
openai_api_key="internal-secret-key",
openai_api_base="http://localhost:8000/v1",
model_name="llama3",
max_tokens=1024
)
# 2. ローカルEmbeddingモデルの設定
# 事前にダウンロードしたパスを指定、もしくは初回起動時にダウンロード(オフラインなら事前配置必須)
embedding_model = HuggingFaceEmbeddings(
model_name="/opt/ai/models/multilingual-e5-large",
model_kwargs={'device': 'cuda'}
)
# 3. ベクトルDBへの接続
client = QdrantClient(url="http://localhost:6333")
vector_store = Qdrant(
client=client,
collection_name="company_docs",
embeddings=embedding_model
)
# 4. RAGチェーンの作成
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=vector_store.as_retriever(search_kwargs={"k": 3}),
return_source_documents=True
)
# 5. 実行
query = "社内のリモートワーク規定について教えて"
result = qa_chain.invoke(query)
print("回答:", result['result'])
print("参照元:", [doc.metadata['source'] for doc in result['source_documents']])
このコードの重要な点は、全てのコンポーネントが localhost または指定されたローカルパスを参照していることです。外部へのリクエストは一切発生しません。
4. セキュリティとガバナンス:監査ログとフィルタリングの実装
「オフラインだから安全」というのは、あくまで外部からの攻撃に対する側面に過ぎません。内部不正や、LLMが不適切な回答を生成するリスク(ハルシネーションや有害表現)に対する「ガバナンス」は別途構築する必要があります。
LLM Guard等のセキュリティツール導入
プロンプトインジェクション(AIを騙して不適切な出力をさせる攻撃)や、PII(個人情報)の流出を防ぐために、LLM Guard のようなセキュリティミドルウェアをアプリケーション層に組み込むことを強く推奨します。
例えば、ユーザーからの入力に「マイナンバー」や「クレジットカード番号」が含まれていないかを正規表現や専用モデルでチェックし、含まれている場合はマスキングするか、リクエスト自体を拒否するロジックを実装します。
# 擬似コードによるPIIフィルタリング例
import re
def filter_pii(text):
# 簡易的なクレジットカード番号検出パターン
credit_card_pattern = r'\b(?:\d[ -]*?){13,16}\b'
if re.search(credit_card_pattern, text):
raise ValueError("セキュリティ警告: クレジットカード番号が含まれている可能性があります。")
return text
user_input = "私のカード番号は 1234-5678-9012-3456 です"
try:
safe_input = filter_pii(user_input)
# LLMへのリクエスト処理...
except ValueError as e:
print(e)
より高度な対策としては、Microsoftの「Presidio」などをコンテナとして立ち上げ、API経由でテキストのサニタイズを行う構成が一般的です。
全利用ログの記録と監査用ダッシュボード
企業への導入において極めて重要なのが、「誰が、いつ、何をAIに聞いたか」という証跡管理です。vLLMのアクセスログだけでなく、アプリケーションレベルでの構造化ログを保存する仕組みを整えましょう。
データベースには以下のカラムを含めるべきです。
timestamp: 実行日時user_id: 社員IDinput_prompt: 入力プロンプト(PIIマスキング済み)output_response: AIの回答tokens_usage: 消費トークン数(コスト配賦用)latency_ms: 応答時間
これらをFluentdなどで収集し、ElasticsearchやLokiに送ることで、セキュリティチームが常時監視できるダッシュボードを構築します。「不審なプロンプト」を検知してアラートを発報する仕組みがあれば、内部統制の観点からも強固なシステムとなります。
5. 運用とスケーリング:継続的なモデル更新とパフォーマンス維持
システムは構築して終わりではありません。AIモデルの進化は非常に速いため、導入後の運用フェーズにおける「鮮度維持」と丁寧なサポート体制が鍵となります。
モデルのバージョン管理戦略
「新しいモデルがリリースされた際、どのようにサービスを停止させずにモデルを入れ替えるか」という課題に対しては、Blue-Greenデプロイメントの考え方を応用します。
vLLMのコンテナをもう一つ(Green環境)立ち上げ、新しいモデルをロードしてテストを実施します。問題がなければ、ロードバランサー(Nginxなど)の振り分け先を切り替えます。
Docker Composeであれば、以下のようにポートをずらして新旧環境を並存させることが容易に行えます。
- 現行(Blue): Port 8000 (Llama 3)
- 新系(Green): Port 8001 (Llama 4)
PrometheusとGrafanaによるGPU監視
ローカルLLMの運用において注意すべき点の一つが、「VRAM不足」によるクラッシュです。これを未然に防ぐために、dcgm-exporter を導入してGPUの詳細なメトリクスをPrometheusで収集する体制を整えましょう。
監視すべき重要指標は以下の通りです。
- GPU Memory Usage: VRAMの使用率。常に95%を超えている場合は、モデルの量子化レベルを下げるか、GPUリソース増強のサインです。
- GPU Utilization: 計算コアの使用率。これが低いにもかかわらずレスポンスが遅い場合、CPUやディスクI/Oがボトルネックになっている可能性があります。
- Request Latency: ユーザーの体感速度。
まとめ:自律したAIインフラが競争力を生む
ここまで、外部通信を一切行わずに、エンタープライズレベルのAI環境を構築する手順を解説してきました。
- vLLM で高速かつ互換性のある推論サーバーを構築する。
- 量子化モデル を活用して、現実的なハードウェアコストで高性能を実現する。
- Docker で環境をパッケージ化し、エアギャップ環境へのデプロイを可能にする。
- RAG と 監査ログ で、実務への応用とガバナンスを両立させる。
このアプローチは、SaaS契約に比べて初期構築の手間はかかります。しかし、構築した環境は、外部の規約変更や価格改定に振り回されることなく、自社のセキュリティポリシーに合わせて自由にカスタマイズできる「資産」となります。
まずは手元にあるワークステーションなどの環境から検証を始めてみてはいかがでしょうか。LANケーブルを抜き、Dockerをインストールし、ローカルLLMの構築に取り組むことで、制約から解放された自由なAI活用の可能性が広がっていくはずです。
コメント