Ollama Embedding 実践:ローカルベクトル検索と RAG 構築
先週、PC の中にある 200 枚以上の PDF 文書を探し回りました。半年前に読んだ技術資料の詳細を探したかったんです。キーワード検索?ダメでした。覚えているのは内容の意味で、原文じゃないんです。見つけるのに 1 時間近くかかりました。その時思いました。「意味を理解できる」検索ツールがあればいいのにって。
もっと困ったのは、これらの文書が会社の内部アーキテクチャに関わっていること。クラウドでベクトル検索?考えられません。プライバシーの赤線は厳しいです。
その後、Ollama の Embedding 機能がちょうどこの問題を解決できると分かりました。ローカルで動く、データは外に出ない、セマンティック検索もできる。数日間試して、mxbai、nomic、Qwen3 の 3 つのモデルを全部試しました。ついでにベクトルデータベースの選定も徹底的に調べました。正直、落とし穴は結構ありました。モデルを間違えると検索効果が悪いし、データベースを小さく選ぶと後で拡張が大変です。
この記事では、私の経験をまとめます。読み終われば、自分でローカル RAG システムを構築できます。文書処理からセマンティック検索まで、全プロセスのコードを提供します。
Ollama Embedding モデルラインナップ
Ollama は今、いくつかの Embedding モデルに対応しています。最初はどれを選べばいいか分かりませんでした。公式ドキュメントでは mxbai が OpenAI の text-embedding-3-large を超えたと言っています。確かにすごそうですが、実際どうなんでしょう?一通り試して、答えを出しました。
まず、この比較表を見てください。
| モデル | ベクトル次元 | コンテキスト長 | モデルサイズ | 特徴 |
|---|---|---|---|---|
| mxbai-embed-large | 1024 | 512 トークン | 670M | 汎用的な第一選択、MTEB ランキング上位 |
| nomic-embed-text | 768 | 8192 トークン | 274M | 長文対応、コンテキスト拡張対応 |
| Qwen3 Embedding | 1024 | 8192 トークン | 約 600M | 2026 年新発売、中国語対応 |
mxbai-embed-large は一番よく使っています。なぜか?簡単で使いやすく、効果も安定しています。1024 次元のベクトルはほとんどの場面で十分です。MTEB(Massive Text Embedding Benchmark)ランキングでも上位に入っています。確かに OpenAI の text-embedding-3-large より少し高いスコアです。日常の文書検索やコード検索のようなタスクなら、これを選べば間違いありません。
nomic-embed-text の特徴は 8192 トークンのコンテキスト長です。記事全体や長い対話ログを処理する場合に便利です。サイズも小さく、274M パラメータで、mxbai より少し速く動きます。ただし、ベクトル次元が 768 に下がっているので、意味表現力は理論的に少し弱くなります。実際にテストしてみると、短文検索では差があまりなく、長文のシーンでは nomic が適しています。
Qwen3 Embedding はアリババが 2026 年 4 月にリリースしたばかりです。中国語の効果は確かにいいです。技術記事をいくつかテストしました。「分散システムのフォールトトレランスメカニズム」と「フォールトトレランス設計」がマッチしました。mxbai は少し苦戦しています。主に中国語のコンテンツを処理するなら、これを試してみる価値があります。
選び方のアドバイス?初心者は迷わず mxbai で OK です。長文のシーンなら nomic、中国語コンテンツなら Qwen3 を優先です。要するに、3 つ全部試しても 30 分程度です。効果を見るのが一番です。
ベクトルデータベース選定ガイド
モデルが決まったら、データはどこに保存する?ベクトルデータベースの選択はモデルより落とし穴が多いです。小さく選ぶと後で拡張が大変、大きく選ぶとリソースの無駄です。3 つの主流な方法を比較しました。
| データベース | 適用シーン | データ規模 | 特徴 |
|---|---|---|---|
| ChromaDB | 入門開発、個人プロジェクト | < 10万件 | 即座に使用、設定不要 |
| FAISS | 単機高性能、研究実験 | 10-100万件 | Meta製オープンソース、超高速 |
| Milvus | 本番デプロイ、エンタープライズ | 百万+件級 | 分散、拡張可能、機能充実 |
ChromaDB は最もおすすめの入門選択です。インストールは 1 コマンドで完了:pip install chromadb。API 設計も使いやすく、データの保存や検索は数行のコードで終わります。HNSW(Hierarchical Navigable Small World)インデックスを使用していて、小規模データの検索速度は十分です。デメリットは単機デプロイで、データが 10 万件を超えると性能が落ち始めます。
FAISS は Meta がオープンソース化した老舗ツールです。純粋な C++ 実装で、速度は本当に速いです。50 万件のデータをテストしましたが、検索遅延はミリ秒単位で安定しています。ただし、これは完全なデータベースではなく、ベクトル検索ライブラリに近いです。ストレージやインデックスファイルを自分で管理する必要があります。カスタマイズしたい人や、性能にこだわるシーンに適しています。
Milvus は全く違います。本番環境向けです。分散デプロイ、永続化ストレージ、複数インデックスタイプに対応しています。クラウド版(Zilliz Cloud)もあります。ただし、設定が複雑で、デプロイコストも高いです。百万規模のデータ、高可用性、チーム連携が必要な場面で初めて投入する価値があります。
私の選択戦略:個人で遊ぶなら ChromaDB、すぐに動かせます。研究プロジェクトや性能重視なら FAISS。本番に出すなら、Milvus かクラウドサービスを直接使います。ChromaDB から Milvus に後で移行する考えはやめましょう。データ形式や API が違うので、移行コストは結構高いです。
完全な RAG フロー実践
理論はこれくらいで、コードを見ていきましょう。以下は完全なローカル RAG 実装です。PDF 文書からセマンティック検索まで、Ollama + ChromaDB で構築します。
環境準備
まず依存関係をインストールします:
pip install ollama chromadb langchain langchain-community pypdf
Ollama が動いていること、モデルもダウンロード済みであることを確認します:
ollama pull mxbai-embed-large
ollama pull qwen2.5:7b # 回答生成用
コード実装
import ollama
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
import chromadb
# 1. PDF 文書を読み込む
loader = PyPDFLoader("./your_document.pdf")
docs = loader.load()
# 2. 文書を分割する — チャンクサイズは大事、大きすぎると検索が不正確、小さすぎると情報が失われる
splitter = RecursiveCharacterTextSplitter(
chunk_size=800, # 各チャンク 800 文字
chunk_overlap=100, # 100 文字重複、境界情報の損失を防ぐ
)
chunks = splitter.split_documents(docs)
# 3. Embedding を生成して ChromaDB に保存
client = chromadb.Client()
collection = client.create_collection("my_docs")
for i, chunk in enumerate(chunks):
# Ollama API を呼んでベクトルを生成
response = ollama.embed(
model="mxbai-embed-large",
input=chunk.page_content,
)
embedding = response["embeddings"][0]
# ベクトルデータベースに保存
collection.add(
ids=[str(i)],
embeddings=[embedding],
documents=[chunk.page_content],
metadatas=[{"source": chunk.metadata.get("source", "unknown")}],
)
print(f"{len(chunks)} 個の文書フラグメントを保存しました")
# 4. セマンティック検索
query = "分散システムのフォールトトレランスメカニズムとは?"
query_embedding = ollama.embed(
model="mxbai-embed-large",
input=query,
)["embeddings"][0]
results = collection.query(
query_embeddings=[query_embedding],
n_results=3, # 最も関連性の高い 3 つのフラグメントを返す
)
# 5. 検索結果で回答を生成
context = "\n\n".join(results["documents"][0])
response = ollama.chat(
model="qwen2.5:7b",
messages=[
{
"role": "system",
"content": "以下の文書内容に基づいて質問に答えてください。文書に関連情報がない場合は、正直に説明してください。",
},
{"role": "user", "content": f"文書内容:{context}\n\n質問:{query}"},
],
)
print(f"回答:{response['message']['content']}")
実行してみてください。全体のフローは実は複雑ではありません。文書分割 → ベクトル生成 → 保存 → 検索 → 回答の組み立てです。
いくつかの注意点:
一つ目は chunk_size を適当に設定しないことです。200 文字ずつに分割してみましたが、検索結果は断片的な情報ばかりで、完全な答えになりませんでした。500-1000 の範囲が安定しています。
二つ目はバッチ処理です。文書量が多い場合、Ollama API を 1 件ずつ呼ぶと遅くなります。まとめて処理できます:
# バッチで Embedding 生成、速度向上が明らか
batch_texts = [chunk.page_content for chunk in chunks[:50]]
batch_embeddings = ollama.embed(
model="mxbai-embed-large",
input=batch_texts,
)["embeddings"]
三つ目は類似度閾値です。ChromaDB はデフォルトで n_results 個の結果を返します。関連性の有無は問いません。一部のシーンでは無関係な結果をフィルタリングする必要があります:
# カスタム距離閾値でフィルタリング
results = collection.query(
query_embeddings=[query_embedding],
n_results=10,
)
# 距離が 0.3 未満の結果だけ保持(距離が小さいほど類似)
filtered = [
doc for doc, dist in zip(results["documents"][0], results["distances"][0])
if dist < 0.3
]
このコードを実行すれば、自分のローカル RAG が完成です。文書を PDF に変え、query を聞きたいことに変えれば、他はそのままで OK です。
パフォーマンスチューニングと実践アドバイス
システムが動き始めたら、次はチューニングです。いくつかのパラメータが直接効果に影響します。私が経験した落とし穴を全部お伝えします。
chunk_size はどれくらい?
500-1000 文字が私の経験値です。小さすぎる落とし穴は意味が不完全になること。一文が半分に切れて、検索結果が質問とマッチしないです。大きすぎる落とし穴は検索ノイズです。一つのチャンクに複数のトピックが含まれ、境界が曖昧になります。
文書タイプによっても差があります。技術文書は構造が明確なので、段落で分割できます。対話ログのような断片的なものは、500 文字ずつが適しています。実際のシーンで試してみてください。普遍的な答えはありません。
バッチ処理で高速化
Ollama API を 1 件ずつ呼ぶと、毎回ネットワーク往復を待つ必要があります。50-100 件まとめて送ると、速度は数倍上がります。ただし大きすぎないように。Embedding モデルには入力長の制限があり、超えるとエラーになります。
類似度閾値はどう決める?
0.7-0.85 の範囲で、正確性への要求によります。閾値を高く(例えば 0.85)設定すると、非常に相关な結果だけを保持し、再現率は低いですが正確です。閾値を低く(0.7)設定すると、再現は多いですがノイズが混ざる可能性があります。文書ライブラリがきれいで質問が明確なら、閾値を高く。質問が曖昧でより多くの情報が必要なら、閾値を低く。
一つのアドバイス:まず 50-100 件のデータで 1 回実行して、検索効果を見てからパラメータを決めてください。全データを入れてから調整すると、時間コストが大きすぎます。反復的にチューニングして、動かしながら修正しましょう。
まとめ
いろいろ言いましたが、核心はいくつかです。
モデル選択はシーンによります。汎用なら mxbai、長文なら nomic、中国語なら Qwen3 を優先です。データベースは入門に ChromaDB、性能重視なら FAISS、本番環境なら Milvus です。完全なフローのコードは全部あります。少し修正すれば動きます。
この方法のメリットは明確です。ローカルデプロイでデータプライバシーが管理できます。Ollama モデルは無料でコストゼロ。ChromaDB は簡単で始めるハードルが低いです。デメリットもあります。単機の性能上限が限られ、データが百万を超えるとアップグレードを検討する必要があります。
まず記事のコードを 1 回実行して、50 の文書を入れて効果を試してみることをお勧めします。検索品質がいいか、回答が正確かは、実際にテストしないと分かりません。パラメータが調整できたら、全データに拡張しましょう。
LangChain と Ollama の連携でより複雑なアプリケーションを作る方法について詳しく知りたい方は、以前書いた「LangChain + Ollama 統合実践」をチェックしてみてください。あちらでは対話チェーンとツール呼び出しについて話しています。今回はベクトル検索に焦点を当てています。2 つ合わせれば、完全なローカル LLM アプリ開発ロードマップになります。
ローカル RAG システム構築
Ollama + ChromaDB でローカルベクトル検索システムを構築
⏱️ 目安時間: 30 分
- 1
ステップ1: 依存関係のインストールとモデル準備
以下のコマンドを実行して依存関係をインストールします:
```bash
pip install ollama chromadb langchain langchain-community pypdf
ollama pull mxbai-embed-large
ollama pull qwen2.5:7b
```
Ollama サービスが起動していることを確認してください。 - 2
ステップ2: 文書の読み込みと分割
PyPDFLoader で PDF を読み込み、RecursiveCharacterTextSplitter で分割します:
```python
loader = PyPDFLoader("./your_document.pdf")
docs = loader.load()
splitter = RecursiveCharacterTextSplitter(
chunk_size=800,
chunk_overlap=100,
)
chunks = splitter.split_documents(docs)
```
chunk_size は 500-1000 文字を推奨します。 - 3
ステップ3: ベクトル生成とデータベース保存
Ollama API を呼んで Embedding を生成し、ChromaDB に保存します:
```python
client = chromadb.Client()
collection = client.create_collection("my_docs")
for i, chunk in enumerate(chunks):
response = ollama.embed(
model="mxbai-embed-large",
input=chunk.page_content,
)
embedding = response["embeddings"][0]
collection.add(
ids=[str(i)],
embeddings=[embedding],
documents=[chunk.page_content],
)
```
文書量が多い場合はバッチ処理を使用してください。 - 4
ステップ4: セマンティック検索と回答生成
クエリをベクトルに変換し、関連文書を検索し、LLM で回答を生成します:
```python
query_embedding = ollama.embed(
model="mxbai-embed-large",
input=query,
)["embeddings"][0]
results = collection.query(
query_embeddings=[query_embedding],
n_results=3,
)
context = "\n\n".join(results["documents"][0])
response = ollama.chat(
model="qwen2.5:7b",
messages=[
{"role": "system", "content": "文書内容に基づいて質問に答える"},
{"role": "user", "content": f"文書:{context}\n質問:{query}"},
],
)
```
必要に応じて類似度閾値で結果をフィルタリングしてください。
FAQ
Ollama の Embedding モデルでどれが一番使いやすい?
• mxbai-embed-large:汎用的な第一選択、効果が安定、ほとんどのシーンに適用
• nomic-embed-text:長文のシーン、8192 トークン対応
• Qwen3 Embedding:中国語対応、2026 年新発売
3 つ全部試して、実際の効果を基準にすることをお勧めします。
ChromaDB、FAISS、Milvus はどれを選ぶべき?
• ChromaDB:入門用、設定不要、10 万件以内に適用
• FAISS:性能優先、単機百万級、ストレージ管理が自分で必要
• Milvus:本番環境、分散デプロイ、百万級以上のデータ
個人プロジェクトなら ChromaDB、本番環境なら Milvus を使用してください。
chunk_size はどれくらいに設定すべき?
Embedding 生成速度をどうやって上げる?
類似度閾値はどれくらいに設定すべき?
ローカル RAG の主なメリットとデメリットは?
デメリット:単機の性能に限りがある、百万級以上のデータはアップグレードが必要、サービスのメンテナンスが自分で必要。
6 min read · 公開日: 2026年4月8日 · 更新日: 2026年4月8日
関連記事
LangChain + Ollama 統合ガイド:ローカル LLM アプリ開発完全マニュアル
LangChain + Ollama 統合ガイド:ローカル LLM アプリ開発完全マニュアル
Ollama マルチモデルデプロイ:Qwen、Llama、DeepSeek の並列実行
Ollama マルチモデルデプロイ:Qwen、Llama、DeepSeek の並列実行
Ollama Modelfile パラメータ詳解:カスタムモデル作成の完全ガイド

コメント
GitHubアカウントでログインしてコメントできます