現実の問題:社内文書をクラウドに上げられないとき
自分の会社には大量の技術文書があった。運用手順書、契約書、機器マニュアルといった数十件のPDFやWordファイルだ。調べ物のたびにファイルを開き、Ctrl+F で検索しては行ったり来たり。単純な質問一つに1時間近く費やすこともあった。
最初に思いついた解決策はChatGPTにアップロードして質問することだった。しかしブラウザにファイルをドラッグした瞬間、IT部門の責任者が止めに入った――文書には顧客情報や機密契約が含まれており、OpenAIのサーバーに送信するわけにはいかないと。
そこでLlamaIndexとOllamaの組み合わせを調べ始めた。社内サーバーで完全にAIを動かし、データの一バイトも社外に出さない構成だ。
文書に「質問する」ための3つのアプローチ
技術的な詳細に入る前に、3つのアプローチを率直に比較して、課題に合った適切なツールを選べるようにしたい。
方法1 — ChatGPT/Claudeに直接アップロード
最もシンプルな方法:PDFをChatGPTにドラッグして質問するだけ。インストール不要で即座に使える。
- メリット:コード不要、即セットアップ、回答品質が高い
- デメリット:データがOpenAI/Anthropicのサーバーに送信される。ファイルサイズ制限あり。セッションごとに再アップロードが必要。機密文書には不向き。
方法2 — LangChain RAGとクラウドLLM
LangChainでRAGパイプラインを構築し、OpenAI APIをLLMとして使用する。多くの開発者に馴染みのあるアプローチだ。
- メリット:柔軟性が高く、エコシステムが豊富で、ドキュメントも充実している
- デメリット:クエリ(および関連文書の断片)は依然クラウドに送信される。トークン従量課金でAPIコストがかかる。インターネット接続が必要。
方法3 — LlamaIndex + Ollama(完全オフライン)
LlamaIndexは文書のインデックス作成とクエリに特化したフレームワークだ。Ollamaでローカルに動かすことで、パイプライン全体が100%自分のマシン上で完結する。
- メリット:データを外部に送信しない。API料金不要。セットアップ後はインターネット不要で動作。
- デメリット:十分なハードウェアが必要。ローカルモデルの品質はGPT-4に及ばないことが多い。
LlamaIndexはLangChainと何が違うのか?
LangChainは汎用フレームワークだ――エージェント、チェーン、ツール呼び出し、RAGなど多くのことをこなせる。しかしすべてをカバーしようとするあまり、APIが頻繁に変わり、純粋な文書処理という課題には必要以上に複雑になりがちだ。
LlamaIndexははじめから一つの課題だけを解くために設計されている:自分のデータとLLMをつなぐこと。より明確な概念を持っている:
- Document Loaders:PDF、Word、CSV、Notion、Google Docsなど数十のコネクタが標準で利用可能
- Node Parser:文書を制御された方法でチャンクに分割
- Index:複数の種類のインデックス(VectorStoreIndex、SummaryIndex、KnowledgeGraphIndexなど)
- Query Engine:文書コンテキストを使って質問を処理し回答する
純粋な文書Q&Aという課題では、LlamaIndexは通常コードが大幅に少なくて済む。次のステップのコードはわずか15行程度だ――LangChainで同等のものを書くと通常その倍かかり、しかもどのAPIが非推奨になったかを追い続ける必要がある。
LlamaIndex + Ollamaを選ぶべきタイミング
このソリューションが適しているのは次のような場合だ:
- 機密性の高い社内文書がある(契約書、NDA、顧客データ、営業機密)
- インターネットのない環境やイントラネット内で作業している
- 毎日大量の質問があり、APIコストを削減したい
- 社内サーバー(オンプレミス)で動作するシステムが必要
文書が機密でなく素早くテストしたいだけなら、ChatGPTの方がずっと速い。過剰設計はしないこと。
実践的な導入ガイド
ステップ1 — Ollamaのインストールとモデルのpull
# Ollamaのインストール(Linux/Mac)
curl -fsSL https://ollama.com/install.sh | sh
# 軽量モデルをpull(強力なGPUがないマシン向け)
ollama pull llama3.2
# 専用embeddingモデルをpull(共用より品質が高い)
ollama pull nomic-embed-text
# モデルが準備できているか確認
ollama list
モデルはhttp://localhost:11434で起動します――LlamaIndexはここに接続します。
ステップ2 — LlamaIndexと依存パッケージのインストール
pip install llama-index llama-index-llms-ollama llama-index-embeddings-ollama
# PDFとWordのパーサー
pip install llama-index-readers-file python-docx pypdf
ステップ3 — PDFとWordの読み込み、インデックスの作成
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from llama_index.llms.ollama import Ollama
from llama_index.embeddings.ollama import OllamaEmbedding
from llama_index.core import Settings
# OpenAIの代わりにOllamaを使用するよう設定
Settings.llm = Ollama(model="llama3.2", request_timeout=120.0)
Settings.embed_model = OllamaEmbedding(model_name="nomic-embed-text")
# PDFやWordをすべて./documents/フォルダに配置
# SimpleDirectoryReaderがファイル形式を自動認識
documents = SimpleDirectoryReader("./documents").load_data()
# 文書からベクトルインデックスを作成
index = VectorStoreIndex.from_documents(documents)
# 試しに質問してみる
query_engine = index.as_query_engine()
response = query_engine.query("契約書2024-001の保証条項は何ですか?")
print(response)
ステップ4 — 毎回再ビルドしないようにインデックスを保存
インデックスの初回ビルドには数分かかることがある――PDF50ファイルの場合、CPUで3〜5分程度だ。保存しておけば、次回は瞬時にロードできる:
from llama_index.core import StorageContext, load_index_from_storage
import os
PERSIST_DIR = "./storage"
if not os.path.exists(PERSIST_DIR):
# 初回:ビルドしてディスクに保存
documents = SimpleDirectoryReader("./documents").load_data()
index = VectorStoreIndex.from_documents(documents)
index.storage_context.persist(persist_dir=PERSIST_DIR)
else:
# 2回目以降:ディスクからロード、再処理不要
storage_context = StorageContext.from_defaults(persist_dir=PERSIST_DIR)
index = load_index_from_storage(storage_context)
query_engine = index.as_query_engine()
ステップ5 — 社内Webサイト(イントラネット)からの読み込み
社内Wiki、Confluenceのドキュメント、あるいは社内ネットワークからしかアクセスできないWebサイトがある場合:
from llama_index.readers.web import SimpleWebPageReader
from llama_index.core import SimpleDirectoryReader
# 社内URLからロード――社内ネットワーク内から実行
loader = SimpleWebPageReader(html_to_text=True)
web_docs = loader.load_data(urls=[
"http://wiki.internal/sop/quy-trinh-xuat-hang",
"http://wiki.internal/sop/kiem-tra-chat-luong",
])
# ファイル文書と組み合わせる
file_docs = SimpleDirectoryReader("./documents").load_data()
all_docs = web_docs + file_docs
index = VectorStoreIndex.from_documents(all_docs)
ステップ6 — チーム全員が使えるシンプルなインターフェース
# simple_qa.py
import sys
from llama_index.core import StorageContext, load_index_from_storage
from llama_index.llms.ollama import Ollama
from llama_index.embeddings.ollama import OllamaEmbedding
from llama_index.core import Settings
Settings.llm = Ollama(model="llama3.2", request_timeout=120.0)
Settings.embed_model = OllamaEmbedding(model_name="nomic-embed-text")
query = " ".join(sys.argv[1:])
if not query:
print("使い方: python simple_qa.py <質問を入力>")
sys.exit(1)
storage_context = StorageContext.from_defaults(persist_dir="./storage")
index = load_index_from_storage(storage_context)
query_engine = index.as_query_engine(similarity_top_k=3)
response = query_engine.query(query)
print(f"\n回答:\n{response}\n")
print("出典:", [n.metadata.get("file_name", "web") for n in response.source_nodes])
python simple_qa.py "顧客ABCとの契約はいつ満了しますか?"
source_nodesによってAIがどの文書のどのページから情報を取得したかがわかる。これがRAGの強みだ:出典をすぐに検証できるため、AIを盲信しなくて済む。純粋なLLMではこれができない。
本番導入時に見落とされがちなポイント
以下の2点は最もよく見落とされるが、結果の品質に最も大きく影響する:
チャンクサイズが精度を左右する。LlamaIndexのデフォルトは1024トークン/チャンクだ。機器マニュアル、スペックシート、運用手順書といった情報密度の高い技術文書では、チャンク512トークン・オーバーラップ50にすることで精度が明らかに向上することが多い:
from llama_index.core.node_parser import SentenceSplitter
from llama_index.core import Settings
Settings.node_parser = SentenceSplitter(chunk_size=512, chunk_overlap=50)
スキャンした文書(画像PDF)はOCRなしでは読み込めない。LlamaIndexはテキストレイヤーしか読まない。請求書、古い契約書、印刷してスキャンし直した書類など、画像スキャンのPDFは事前にOCR処理が必要だ。Tesseract(オープンソース、無料)またはIBMのDoclingで対応できる。
3つの選択肢の比較
| 比較項目 | ChatGPT アップロード | LangChain + クラウドLLM | LlamaIndex + Ollama |
|---|---|---|---|
| データセキュリティ | クラウドに送信 | クエリをクラウドに送信 | 100%ローカル |
| 運用コスト | サブスクリプション | トークン従量課金 | 無料(ハードウェアのみ) |
| セットアップ難易度 | 不要 | 中程度 | 中程度 |
| 回答品質 | 最高 | 高い | モデルに依存 |
| オフライン動作 | 不可 | 不可 | 可能 |
現在使っているシステムは約200ファイル――機器マニュアルのPDF、契約書、社内SOP――を処理している。llama3.2をCPUのみで動かした場合のレスポンスタイムは1質問あたり8〜15秒程度だ。ChatGPTより遅いが、チームの誰も社内データが外部のサーバーに置かれているのを心配する必要がない。
社内文書という課題はほぼすべての企業に存在する――シフトごとに機器マニュアルを検索しなければならない工場から、数百件の契約から条項を探し出さなければならないオフィスまで。セキュリティが妥協できない制約であれば、LlamaIndex + Ollamaは最初に試すべきスタックだ。

