「ベクター検索専用のDBに移行したいが、OllamaのEmbeddingとどう繋げればいいのかわからない」
そんな悩みを抱えるインフラエンジニアやMLOps担当者は多いはずです。この記事では、Rustで書かれた高性能ベクターデータベース「Qdrant」をUbuntu ServerにDockerで構築し、OllamaのEmbedding APIと連携してローカルRAGパイプラインを実装する手順を解説します。
pgvectorとの使い分け判断から、コレクション作成・ドキュメント登録・セマンティック検索・LLMによる回答生成まで、インターネットにデータを一切送らず社内完結で実現する方法を網羅します。
この記事のポイント
・nomic-embed-textのEmbeddingとQdrantを組み合わせてローカルRAGを構築できる
・docker runコマンド1つでQdrantサービスを起動し、Python qdrant-clientでコレクションを作成する
・pgvectorと異なり専用ベクターDBのためドキュメント数が増えてもミリ秒レベルの検索速度を維持できる
・本番運用時はQdrant Web UIとREST APIでコレクション状態を監視・管理する
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
QdrantとpgvectorをRAGで使い分ける判断基準
RAG構築でよく比較されるのが、PostgreSQL拡張のpgvectorとQdrantです。どちらもOllamaのEmbedding APIと連携できますが、設計思想が根本的に異なります。pgvectorはすでにPostgreSQLを使っている環境であれば追加インフラなしで導入でき、SQLで既存の業務データと結合できる点が強みです。一方、Qdrantはベクター検索専用に設計されているため、ANN(近似最近傍)インデックスの実装が最適化されており、ドキュメント数が数万件を超えるとレスポンス速度で明確な差が出てきます。
判断の目安は3点です。まず、社内ドキュメントが数千件程度でSQLによる複合条件検索が必要なら、pgvectorで十分です。ドキュメントが数万件以上になる、あるいは複数ユーザーが同時にクエリを投げる本番ワークフローであれば、Qdrantへの移行を検討する価値があります。さらに、PostgreSQLの運用負荷を切り離したい場合も、専用サービスとして独立したQdrantが管理しやすくなります。
社内でChatGPTが使えない環境にローカルLLMを導入する際の全体設計については、「社内でChatGPTが使えないときの代替手段|機密データを守るローカルLLMという選択肢」も参考にしてください。
UbuntuにDockerでQdrantをインストールする手順
1. 事前確認
DockerがインストールされたUbuntu Server上での作業を前提とします。OllamaのセットアップとUbuntu Serverの基本設定については「Ubuntu ServerでローカルLLMを構築する方法|Ollamaで機密データを外に出さず業務AIを動かす完全ガイド」を参照してください。Dockerのバージョンを確認します。
$ docker --version Docker version 27.3.1, build ce12230
2. データ永続化ディレクトリを作成してQdrantコンテナを起動する
ストレージ用ディレクトリを作成し、QdrantのDockerイメージを起動します。ポート6333はREST APIとWeb UI、ポート6334はgRPC用です。$ mkdir -p ~/qdrant/storage $ docker run -d \ --name qdrant \ --restart unless-stopped \ -p 6333:6333 \ -p 6334:6334 \ -v ~/qdrant/storage:/qdrant/storage \ qdrant/qdrant:latest
3. 起動確認
curlでヘルスチェックAPIを叩き、バージョン情報が返ってくれば正常です。$ curl -s http://localhost:6333/ | python3 -m json.tool { "title": "qdrant - vector search engine", "version": "1.13.4", "commit": "..." }
`--restart unless-stopped` を付けておくと、サーバー再起動後もDockerデーモンが自動的にQdrantコンテナを再開します。これはsystemdサービスとして常時起動させるOllamaと同様の運用思想です。
Ollamaでnomic-embed-textのEmbeddingモデルを準備する手順
1. Embeddingモデルを取得する
Ollamaのembedding用途として `nomic-embed-text` を使います。このモデルは768次元のベクターを生成し、英語・日本語を含む多言語テキストに対応しています。モデルサイズは約274MBとコンパクトで、CPUのみの環境でも実用的な速度で動作します。どのEmbeddingモデルが自分の用途に合うかを先に整理したい場合は、「ローカルLLMのモデルを比較する方法|Llama3.3・Mistral・Gemma・Phi-4をUbuntuで使い分けるポイント」が参考になります。
$ ollama pull nomic-embed-text pulling manifest pulling 970aa74c0a90... 100% ▕████████████████████▏ 274 MB verifying sha256 digest writing manifest success
2. Embedding APIの動作を確認する
Ollama 0.4以降のEmbedding APIエンドポイントは `/api/embed`(単数形)です。テキストを渡して768次元の配列が返ってくれば正常です。$ curl -s http://localhost:11434/api/embed \ -d '{"model":"nomic-embed-text","input":"Ollamaのテスト文章です"}' \ | python3 -c "import sys,json; d=json.load(sys.stdin); print(f'次元数: {len(d[\"embeddings\"][0])}')" 次元数: 768
PythonでQdrantコレクションを作成してドキュメントを登録する手順
1. 必要なライブラリをインストールする
$ pip install qdrant-client requests
2. コレクションを作成するスクリプト
nomic-embed-textは768次元のため `size=768` を指定します。類似度計算方式は `Distance.COSINE`(コサイン類似度)を使います。テキスト検索では内積(DOT)より安定した結果が得られます。# create_collection.py from qdrant_client import QdrantClient from qdrant_client.models import Distance, VectorParams client = QdrantClient(host="localhost", port=6333) client.create_collection( collection_name="local_docs", vectors_config=VectorParams(size=768, distance=Distance.COSINE), ) print("コレクション作成完了")
$ python3 create_collection.py コレクション作成完了
3. ドキュメントをベクター化して登録するスクリプト
OllamaのEmbedding APIで各ドキュメントを768次元のベクターに変換し、テキスト本文をpayloadとして一緒に格納します。payloadは検索結果から取り出すための任意のメタデータです。# ingest_docs.py import requests from qdrant_client import QdrantClient from qdrant_client.models import PointStruct def get_embedding(text: str) -> list: resp = requests.post( "http://localhost:11434/api/embed", json={"model": "nomic-embed-text", "input": text}, ) return resp.json()["embeddings"][0] client = QdrantClient(host="localhost", port=6333) documents = [ {"id": 1, "text": "OllamaはローカルLLMの実行基盤であり、Linuxで手軽に動作する。"}, {"id": 2, "text": "systemdサービスとして登録すると、サーバー再起動後も自動起動する。"}, {"id": 3, "text": "nomic-embed-textは768次元のベクターを生成し、多言語テキストに対応する。"}, {"id": 4, "text": "Qdrantはコサイン類似度でベクターを比較し、関連度の高い文書を返す。"}, {"id": 5, "text": "RAGパイプラインは検索と生成を組み合わせてLLMの回答精度を高める手法だ。"}, ] points = [ PointStruct( id=doc["id"], vector=get_embedding(doc["text"]), payload={"text": doc["text"]}, ) for doc in documents ] client.upsert(collection_name="local_docs", points=points) print(f"{len(points)} 件のドキュメントを登録しました")
$ python3 ingest_docs.py 5 件のドキュメントを登録しました
セマンティック検索クエリをPythonから実行する手順
1. 検索スクリプトを作成する
クエリテキストをEmbedding APIでベクター化し、Qdrantの `search` メソッドで上位3件の類似ドキュメントを取得します。コサイン類似度のスコアは0.0~1.0の範囲で、0.8以上であれば高い関連度とみなせます。# search_docs.py import requests from qdrant_client import QdrantClient def get_embedding(text: str) -> list: resp = requests.post( "http://localhost:11434/api/embed", json={"model": "nomic-embed-text", "input": text}, ) return resp.json()["embeddings"][0] client = QdrantClient(host="localhost", port=6333) query = "Ollamaを自動起動させる方法は?" results = client.search( collection_name="local_docs", query_vector=get_embedding(query), limit=3, ) for r in results: print(f"スコア: {r.score:.4f} | {r.payload['text']}")
2. 検索結果を確認する
$ python3 search_docs.py スコア: 0.8721 | systemdサービスとして登録すると、サーバー再起動後も自動起動する。 スコア: 0.7033 | OllamaはローカルLLMの実行基盤であり、Linuxで手軽に動作する。 スコア: 0.6248 | nomic-embed-textは768次元のベクターを生成し、多言語テキストに対応する。
SQLのLIKE検索とは異なり、「自動起動」というキーワードが含まれていなくても意味的に近い文書を発見できる点がセマンティック検索の強みです。「サーバー再起動後も動作させる方法は?」という言い換えクエリでも同じドキュメントが上位に来ます。
OllamaのLLMとQdrantを組み合わせたRAGパイプラインを実装する手順
1. 全体フローの設計
RAGパイプラインの流れは2ステップです。まずQdrantで質問に関連するドキュメントを検索し、次にその文書をコンテキストとしてOllamaのLLM(llama3.3:8b-instruct-q4_0など)に渡して回答を生成させます。LLMはコンテキスト外の知識で答えるのではなく、提供された文書の範囲で回答するため、ハルシネーション(でたらめな回答)を抑制できます。社内ドキュメントや独自のナレッジベースを活用するRAGシステムの核心となる部分です。
2. RAGパイプラインスクリプトを作成する
# rag_pipeline.py import requests from qdrant_client import QdrantClient OLLAMA_BASE = "http://localhost:11434" EMBED_MODEL = "nomic-embed-text" LLM_MODEL = "llama3.3:8b-instruct-q4_0" def get_embedding(text: str) -> list: resp = requests.post( f"{OLLAMA_BASE}/api/embed", json={"model": EMBED_MODEL, "input": text}, ) return resp.json()["embeddings"][0] def query_llm(prompt: str) -> str: resp = requests.post( f"{OLLAMA_BASE}/api/generate", json={"model": LLM_MODEL, "prompt": prompt, "stream": False}, ) return resp.json()["response"] client = QdrantClient(host="localhost", port=6333) question = "Ollamaを自動起動させる方法は?" # Step1: 関連ドキュメントをQdrantで検索 results = client.search( collection_name="local_docs", query_vector=get_embedding(question), limit=3, ) context = "\n".join(f"- {r.payload['text']}" for r in results) # Step2: LLMにコンテキスト付きで回答を生成させる prompt = f"""以下の参考情報に基づいて質問に答えてください。参考情報にない内容は「情報がありません」と答えてください。 【参考情報】 {context} 【質問】 {question}""" print("=== 回答 ===") print(query_llm(prompt))
3. 実行結果を確認する
$ python3 rag_pipeline.py === 回答 === Ollamaをサーバー再起動後も自動起動させるには、systemdサービスとして登録する方法が有効です。 /etc/systemd/system/ollama.service にサービスユニットファイルを作成し、 systemctl enable ollama コマンドで自動起動を有効化してください。
Qdrant Web UIとREST APIでコレクションを管理・監視する手順
1. Web UIにアクセスする
QdrantはREST APIのポート6333でWeb管理UIも提供しています。ブラウザで以下のURLを開きます。http://サーバーIPアドレス:6333/dashboard
2. コレクション情報をAPIで確認する
$ curl -s http://localhost:6333/collections/local_docs | python3 -m json.tool { "result": { "status": "green", "vectors_count": 5, "indexed_vectors_count": 5, "points_count": 5, "config": { "params": { "vectors": { "size": 768, "distance": "Cosine" } } } } }
3. 監視で確認すべき項目
`status` が `green` であれば正常です。`yellow` はインデックス再構築中、`red` はエラー状態を示します。`vectors_count` と `indexed_vectors_count` が一致していれば、すべてのベクターが検索インデックスに組み込まれています。大量ドキュメントを一括登録した直後は `indexed_vectors_count` が追いつくまで数秒かかることがあります。
コレクションを削除する場合は `DELETE http://localhost:6333/collections/コレクション名` を叩きます。**注意**: 削除は非可逆操作です。本番環境では必ずバックアップを取ってから実施してください。
トラブルシューティング|よくある問題と対処法
① Qdrantコンテナが起動しない(ポート競合)
ポート6333または6334が他のプロセスに使われていないか確認します。$ ss -tlnp | grep -E '6333|6334'
② Embedding APIが `{"error":"..."}` を返す
Ollama 0.4以降では `/api/embed`(単数形)が正しいエンドポイントです。古いバージョンのコード例では `/api/embeddings`(複数形)が使われていることがありますが、バージョンによっては404になります。$ ollama --version ollama version is 0.9.2
③ セマンティック検索のスコアが低く、期待する文書が上位に来ない
nomic-embed-textは英語中心の事前学習データで訓練されているため、日本語テキストで精度が下がる場合があります。日本語が多い社内ドキュメントには `mxbai-embed-large` への切り替えが有効です。$ ollama pull mxbai-embed-large
④ コンテナ再起動後にデータが消える
バインドマウントのパスが正しいかを確認します。`docker inspect qdrant` で `Mounts` セクションを確認し、`~/qdrant/storage` が実際のホストパスにマウントされているか検証します。Dockerのパーミッション問題が発生した場合は `chmod 755 ~/qdrant/storage` で対処します。まとめ
今回はOllamaのEmbedding APIとQdrantを組み合わせてローカルRAGパイプラインを構築する手順を解説しました。| 操作 | 主なコマンド・スクリプト | ポイント |
|---|---|---|
| Qdrant起動 | docker run -d -p 6333:6333 -v ~/qdrant/storage:/qdrant/storage qdrant/qdrant:latest | --restart unless-stoppedで自動再起動 |
| Embeddingモデル取得 | ollama pull nomic-embed-text | 768次元・多言語対応・約274MB |
| コレクション作成 | client.create_collection(size=768, distance=Distance.COSINE) | モデルの次元数に合わせてsizeを設定 |
| ドキュメント登録 | client.upsert(collection_name="local_docs", points=[...]) | payloadにテキスト本文を格納する |
| セマンティック検索 | client.search(query_vector=get_embedding(query), limit=3) | スコア0.8以上が高関連度の目安 |
| RAGパイプライン | 検索結果をLLMのpromptにcontextとして埋め込む | コンテキスト外の回答を禁じる制約文を追加する |
| 監視 | curl http://localhost:6333/collections/local_docs | status:greenを確認する |
ローカルRAGとOllamaを2日間のハンズオンで体験する
QdrantとOllamaの連携構築を手元の環境で試してみたい方、RAGパイプラインを実際に動かしながら理解したい方に最適なセミナーです。
実機GPU環境でEmbedding・ベクター検索・LLM回答生成を一気通貫で実装できます。実機GPU環境で手を動かしながら習得したい方向けに、「ローカルAIマスターセミナー」を開催しています。
少人数(最大8名)ZOOMハンズオン形式で実施しています。
・Ubuntu ServerでローカルLLMを構築する方法|Ollamaで機密データを外に出さず業務AIを動かす完全ガイド
・社内でChatGPTが使えないときの代替手段|機密データを守るローカルLLMという選択肢
・ローカルLLMのモデルを比較する方法|Llama3.3・Mistral・Gemma・Phi-4をUbuntuで使い分けるポイント
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:OllamaをPodmanコンテナで動かす方法|Rocky Linux/RHELへのDockerなし導入とOpen WebUI連携手順
- この記事の属するカテゴリ:ローカルLLMへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます