深夜2時のプライバシー危機
CTOからのSlack通知が届いたのは深夜2時、それは決して良い知らせではありませんでした。法務部門がクラウドベースのAIパイロット運用にストップをかけたのです。理由は、機密扱いのConfluenceページや機密性の高い製品ロードマップをサードパーティのLLMプロバイダーに送信することが、重大なコンプライアンスリスクになるからでした。彼らはAIのスピードを求めていましたが、データはファイアウォールの内側に留めておく必要がありました。
ここで、ローカルでの検索拡張生成(RAG)スタックが真価を発揮します。モデルホスティングのOllamaとデータオーケストレーション의 LlamaIndexを組み合わせることで、ネットワークから1バイトも外に出すことなく、内部ドキュメントを読み取るシステムを構築できます。私はこのセットアップを導入し、1,500件以上の技術仕様書と4,000件の社内Wikiページを処理してきました。その結果は軽快で信頼性が高く、そして何よりプライベートです。
クイックスタート:5分で構築するプライベートQ&A
次のスタンドアップミーティングが始まる前に、プロトタイプを動かすことができます。まずはPDFが入ったフォルダを用意しましょう。
1. Ollamaのインストールとモデルのプル
公式サイトからOllamaをダウンロードします。実行後、ターミナルを開いて、高性能な8Bパラメータモデルと埋め込み(Embedding)モデルをダウンロードします。
# LLMをプル(Llama 3.1はバランスの取れた優れた選択肢です)
ollama pull llama3.1
# 埋め込みモデルをプル(データの検索に不可欠です)
ollama pull nomic-embed-text
2. Python環境のセットアップ
環境をクリーンに保つために仮想環境を作成します。コアとなる LlamaIndex ライブラリと、特定のOllamaコネクタが必要です。
pip install llama-index llama-index-llms-ollama llama-index-embeddings-ollama
3. ローカルRAGの「Hello World」
app.pyという名前のファイルを作成します。このスクリプトは/dataフォルダをスキャンし、数秒で検索可能なインデックスを構築します。
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, Settings
from llama_index.llms.ollama import Ollama
from llama_index.embeddings.ollama import OllamaEmbedding
# ローカルエンジンの設定
Settings.llm = Ollama(model="llama3.1", request_timeout=120.0)
Settings.embed_model = OllamaEmbedding(model_name="nomic-embed-text")
# ローカルディレクトリからドキュメントを読み込む
documents = SimpleDirectoryReader("./data").load_data()
# 検索可能なインデックスを構築
index = VectorStoreIndex.from_documents(documents)
# 質問する
query_engine = index.as_query_engine()
response = query_engine.query("リモートワークに関するポリシーを教えてください。")
print(response)
仕組みの解説:ローカルエンジンが動く仕組み
LlamaIndexは、散乱したファイルとLLMの間の交通整理役として機能します。プロセスは、読み込み(Loading) -> インデックス作成(Indexing) -> クエリ実行(Querying)というシンプルなパイプラインに従います。
なぜローカルRAGにLlamaIndexなのか?
LangChainは複雑なロジックチェーンに適していますが、LlamaIndexはデータ検索に特化して設計されています。ドキュメントを管理しやすい「チャンク(塊)」に分割するという面倒な作業を自動で行い、それらをベクトル(意味の数学的マップ)に変換してローカルに保存します。質問を投げると、システムはライブラリ全体をLLMに渡すのではなく、ローカルのベクトルストアで見つかった最も関連性の高い3〜4つのスニペットのみを送信します。
ConfluenceとNotionへの接続
社内のナレッジがPDFだけで完結することは稀です。多くはConfluenceやNotionに存在します。これらのデータを取り込むには、LlamaHubのローダーを使用します。テキストを取得するためにAPIトークンが必要ですが、実際の処理は自分のマシン上で行われます。機密データが公開モデルのトレーニングに使用されることはなく、ローカルメモリ内に留まります。
# 例:Confluenceからの取得
from llama_index.readers.confluence import ConfluenceReader
loader = ConfluenceReader(base_url="https://your-domain.atlassian.net/wiki")
documents = loader.load_data(space_key='ENGINEERING')
高度な活用:永続化とパフォーマンス
基本的なスクリプトでは、実行するたびにインデックスを再構築します。これはCPUリソースと時間の無駄です。本番環境向けのツールでは、インデックスをディスクに保存する必要があります。
1. インデックスの保存
import os.path
from llama_index.core import StorageContext, load_index_from_storage
PERSIST_DIR = "./storage"
if not os.path.exists(PERSIST_DIR):
# ドキュメントを読み込んでインデックスを作成し、保存する
documents = SimpleDirectoryReader("./data").load_data()
index = VectorStoreIndex.from_documents(documents)
index.storage_context.persist(persist_dir=PERSIST_DIR)
else:
# 既存のインデックスを読み込む
storage_context = StorageContext.from_defaults(persist_dir=PERSIST_DIR)
index = load_index_from_storage(storage_context)
2. ハードウェア要件
Llama 3.1 8Bをスムーズに動作させるには、適切なリソースが必要です。Mシリーズチップを搭載したMacを使用している場合は、16GB以上のユニファイドメモリを推奨します。WindowsやLinuxユーザーは、RTX 3060や4060など、少なくとも12GBのVRAMを搭載したGPUを使用すべきです。CPUのみのサーバーに制限されている場合は、phi3やtinyllamaモデルを試してください。これらは古いハードウェアでも大幅に高速ですが、知能はわずかに劣ります。
安定稼働のための実践的なヒント
私は数ヶ月かけてこのセットアップを改良してきました。データが増えてもシステムが遅くならないための工夫を紹介します。
- チャンクサイズの最適化: LlamaIndexのデフォルトは1024トークンのチャンクです。高密度な技術マニュアルの場合は、これを512に下げてください。これにより、LLMにより具体的なコンテキストが提供され、「ハルシネーション」が減少します。
- データのフィルタリング: すべてをインデックス化しないでください。Confluenceスペースが2019年の会議メモで散らかっていると、AIが混乱します。LlamaIndexのフィルターを使用して、過去12ヶ月以内に更新されたページのみを含めるようにしましょう。
- 埋め込みモデルの重要性: AIが一般的または不正確な回答を返す場合、通常LLM自体が問題ではありません。多くの場合、埋め込みモデル(nomic-embed-text)が適切なドキュメントスニペットを見つけられなかったことが原因です。LLMを交換する前に、検索ログを確認してください。
- 同時実行ユーザーの管理: Ollamaは効率的ですが、限界があります。50人の従業員が同時にローカルAPIにクエリを投げると、サーバーは著しく低速化します。Redisのようなタスクキューを使用して負荷を管理し、レスポンス時間を3秒以内に抑えるようにしましょう。
ローカルRAGシステムの構築は、単なるプライバシーの確保以上の価値を提供します。それは完全なコントロールを手に入れることです。モデルもインフラもあなたの所有物です。自動スクリプトが一晩で会社全体のアーカイブをインデックス化したために、翌朝2,000ドルのAPI請求書に驚くこともありません。ローカルスタックを使い続ければ、データ流出の心配からようやく解放されるのです。

