OllamaとChromaDBでローカルベクターデータベースを構築する方法|社内ドキュメントの意味検索基盤をLinuxサーバーに設定する手順

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)ローカルLLM > OllamaとChromaDBでローカルベクターデータベースを構築する方法|社内ドキュメントの意味検索基盤をLinuxサーバーに設定する手順
「社内マニュアルをAIで意味検索できるようにしたいが、ベクターデータベースの選び方がわからない」
「ChromaDBとOllamaを連携させる具体的な手順が見つからず、RAG構築の第一歩で立ち止まっている」

そんな悩みを抱えるLinuxサーバー管理者・インフラエンジニアは多いはずです。この記事では、OllamaのEmbedding APIとChromaDBを組み合わせて、完全ローカル動作のベクターデータベースを構築する手順を解説します。インストールから社内ドキュメントのインジェスト・意味検索の実行・永続化設定まで、Pythonスクリプトとともに一通りカバーします。クラウドAPIを一切使わず機密文書を扱いたい方に向けた内容です。

この記事のポイント

・nomic-embed-text をOllamaで動かすカスタムEmbedding関数でChromaDBと連携できる
・pip install chromadb 後、PersistentClientを使えばファイル永続化まで数十行で実装可能
・インジェスト→意味検索の一連の流れをPythonスクリプトのコピペで試せる
・コレクション管理・バックアップ・よくあるエラー対処法も網羅


OllamaとChromaDBでローカルベクターデータベースを構築する方法|社内ドキュメントの意味検索基盤をLinuxサーバーに設定する手順

「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

ChromaDBとOllamaを組み合わせる理由 — ローカルだけで意味検索を実現するアーキテクチャ

従来のキーワード検索(grep・全文検索)は、検索クエリの言葉がドキュメントに文字どおり含まれていないと引っかかりません。「多要素認証の設定手順」というドキュメントを「SSHのセキュリティ設定」というキーワードで探す場合、単語が一致しないため見つけられない、という構造的な限界があります。

意味検索ではテキストを高次元のベクター(数値の配列)に変換し、意味的に近いベクター同士を数学的な距離で比較します。「SSHのセキュリティ」と「多要素認証」のベクターが近い位置に存在すれば、たとえ単語が異なっていても関連文書として返せます。この仕組みの中でベクターの保存と高速な類似検索を担うのがベクターデータベースです。

ChromaDBはPython製のオープンソースベクターストアです。Pinecone・Weaviate・Qdrantといったクラウドサービスと異なり、ローカルサーバーのみで動作するため外部へのデータ送信がありません。APIがシンプルで、インメモリ・ファイル永続化・クライアントサーバー構成の3モードを用途に合わせて切り替えられます。

Ollamaとの組み合わせでは、ベクター変換(Embedding)にもローカルモデルを使います。Embedding専用の軽量モデル(nomic-embed-textなど)をOllamaで動かすことで、OpenAIのEmbedding APIに相当する機能をインターネットなしで実現できます。OllamaのUbuntu Serverへの構築手順はUbuntu ServerでローカルLLMを構築する方法の記事で詳しく解説しています。まだ環境を作っていない場合はそちらを先に確認してください。

事前準備 — Ollamaの動作確認とEmbeddingモデルの取得

作業を始める前に、OllamaのEmbedding APIが正常に応答することを確認します。Ollamaサービスが起動していないと以降のすべての手順が失敗するため、先に疎通確認を済ませておくことが重要です。

1. OllamaのEmbedding APIに疎通確認する

# OllamaのEmbedding APIへcurlで確認する $ curl http://localhost:11434/api/embeddings \ -d '{"model": "nomic-embed-text", "prompt": "テスト"}' {"embedding":[-0.01234, 0.06789, -0.02345, 0.03456, ...]}

`"embedding"` キーに数値配列が返れば正常です。`{"error":"model 'nomic-embed-text' not found"}` が返る場合は次のステップでモデルを取得してください。`Connection refused` が表示される場合はOllamaサービスが起動していないため、`systemctl start ollama` で起動します。

2. nomic-embed-textモデルを取得する

# Embeddingモデルをローカルに取得する $ ollama pull nomic-embed-text pulling manifest pulling 970aa74c0a90... 100% ████████ 274 MB/274 MB verifying sha256 digest writing manifest success

`nomic-embed-text` はEmbedding専用モデル(274MB)です。GPUがないCPU環境でも実用的な速度で動作します。大規模コレクションで精度を優先する場合は `mxbai-embed-large`(670MB)も選択肢になります。モデルの特徴と使い分けについてはローカルLLMのモデルを比較する方法の記事も参考になります。

3. Python環境を確認する

$ python3 --version Python 3.11.9 $ pip3 --version pip 24.0 from /usr/lib/python3/dist-packages/pip (python 3.11)

Python 3.8以上であれば問題ありません。依存関係の競合を防ぐため、venv(仮想環境)を使って作業することを推奨します。

ChromaDBをUbuntu Serverにインストールする手順

ChromaDBはpipで簡単にインストールできます。専用の仮想環境を用意してから進めると、システムのPython環境を汚染しません。

1. 作業ディレクトリと仮想環境を準備する

# 作業ディレクトリを作成して仮想環境を構築する $ mkdir -p /opt/chromadb-project && cd /opt/chromadb-project $ python3 -m venv venv $ source venv/bin/activate (venv) $

2. ChromaDBをpipでインストールする

# ChromaDBとHTTPクライアントをインストールする (venv) $ pip install chromadb httpx Collecting chromadb Downloading chromadb-0.5.20-py3-none-any.whl (589 kB) ... Successfully installed chromadb-0.5.20 httpx-0.27.2

`httpx` はOllamaのEmbedding APIへのHTTPリクエストに使います。`requests` でも代替できますが、`httpx` は非同期対応があり後々の拡張に便利です。

3. インストールを確認する

(venv) $ python3 -c "import chromadb; print(chromadb.__version__)" 0.5.20

バージョン番号が表示されれば正常です。ChromaDB 0.4以降では `PersistentClient` が推奨APIとなっています。古いチュートリアルに残る `chromadb.Client()` は非推奨のため使わないでください。

OllamaのEmbedding APIをChromaDBで使うカスタム関数を実装する

ChromaDBのデフォルトEmbedding関数はscikit-learnベースで動作しますが、Ollamaのローカルモデルでベクター生成するにはカスタム実装が必要です。ChromaDBは `__call__(input: list) -> list` のインターフェースを持つクラスをEmbedding関数として受け付けます。

1. カスタムEmbedding関数クラスを作成する

以下を `/opt/chromadb-project/ollama_embed.py` として保存します。

# /opt/chromadb-project/ollama_embed.py import httpx class OllamaEmbeddingFunction: def __init__(self, model="nomic-embed-text", base_url="http://localhost:11434"): self.model = model self.base_url = base_url def __call__(self, input: list) -> list: embeddings = [] for text in input: res = httpx.post( f"{self.base_url}/api/embeddings", json={"model": self.model, "prompt": text}, timeout=30.0 ) res.raise_for_status() embeddings.append(res.json()["embedding"]) return embeddings

`__call__` の引数 `input` はChromaDB内部から渡される `list[str]` です。1テキストずつOllamaにリクエストを送り、返ってきた `"embedding"` 配列を収集して返します。バッチ処理を高速化したい場合はOllama 0.3以降の `/api/embed` エンドポイント(複数テキストを一括送信可能)への切り替えを検討してください。

2. カスタム関数の動作を確認する

(venv) $ python3 -c " from ollama_embed import OllamaEmbeddingFunction fn = OllamaEmbeddingFunction() result = fn(['テスト文字列']) print(f'次元数: {len(result[0])}') print(f'先頭5値: {result[0][:5]}') " 次元数: 768 先頭5値: [-0.01234, 0.06789, -0.02345, 0.03456, -0.05678]

`nomic-embed-text` は768次元のベクターを生成します。次元数が表示されればOllamaとの連携は正常です。後から `mxbai-embed-large`(1024次元)に切り替える場合、既存コレクションを削除して再インジェストする必要があります。次元数の違うベクターは同一コレクションに混在できません。

社内ドキュメントをChromaDBにインジェストする手順

実際にテキストファイルを読み込んでChromaDBへ格納するスクリプトを作成します。大量ドキュメントを扱う場合は、1ドキュメントあたり300~600字に分割(チャンキング)してから格納すると検索精度が上がります。ここではまず分割なしの基本形から始めます。

1. インジェストスクリプトを作成する

以下を `/opt/chromadb-project/ingest.py` として保存します。

# /opt/chromadb-project/ingest.py import chromadb from ollama_embed import OllamaEmbeddingFunction import os, sys def ingest_docs(doc_dir, collection_name="internal_docs"): embed_fn = OllamaEmbeddingFunction(model="nomic-embed-text") client = chromadb.PersistentClient(path="/opt/chromadb-data") col = client.get_or_create_collection( name=collection_name, embedding_function=embed_fn ) ids, texts = [], [] for fname in sorted(os.listdir(doc_dir)): if not fname.endswith(".txt"): continue with open(os.path.join(doc_dir, fname), "r", encoding="utf-8") as f: text = f.read().strip() if not text: continue ids.append(fname) texts.append(text) print(f" 読み込み: {fname} ({len(text)}字)") if ids: col.add(documents=texts, ids=ids) print(f"\n合計 {len(ids)} 件をChromaDBへ格納完了") else: print("対象ファイルが見つかりません") if __name__ == "__main__": ingest_docs(sys.argv[1] if len(sys.argv) > 1 else "./docs")

2. サンプルファイルでインジェストを実行する

# サンプルドキュメントを作成してインジェストを実行する (venv) $ mkdir -p /opt/chromadb-project/docs (venv) $ echo "rootパスワードは月次で変更すること。変更はpasswdコマンドを使う。" \ > /opt/chromadb-project/docs/security_policy.txt (venv) $ echo "本番サーバーへのSSHは多要素認証を必須とする。鍵認証とOTPを組み合わせる。" \ > /opt/chromadb-project/docs/ssh_policy.txt (venv) $ python3 ingest.py /opt/chromadb-project/docs 読み込み: security_policy.txt (35字) 読み込み: ssh_policy.txt (38字) 合計 2 件をChromaDBへ格納完了

`/opt/chromadb-data` ディレクトリ以下にSQLiteファイルとしてベクターデータが永続化されます。サーバーを再起動しても同じパスを指定すれば再ロードできます。

既存コレクションへの追加時、IDが重複するとエラーになります。再インジェストする場合は `col.delete(ids=[fname])` で既存エントリを削除してから `col.add()` を実行してください。差分インジェスト(変更ファイルのみ更新)をするなら、ファイルのハッシュ値をmetadataとして保存し変更検出する実装が有効です。

PythonからChromaDBで意味検索を実行する

格納したドキュメントに対して意味検索を実行します。検索クエリもOllamaでベクター化され、格納済みベクターとのコサイン距離が計算されます。テキストが一言も一致しなくても、意味が近ければ上位に返ってきます。

1. 検索スクリプトを作成する

以下を `/opt/chromadb-project/search.py` として保存します。

# /opt/chromadb-project/search.py import chromadb from ollama_embed import OllamaEmbeddingFunction import sys def search_docs(query, n_results=3, collection_name="internal_docs"): embed_fn = OllamaEmbeddingFunction(model="nomic-embed-text") client = chromadb.PersistentClient(path="/opt/chromadb-data") col = client.get_collection( name=collection_name, embedding_function=embed_fn ) results = col.query(query_texts=[query], n_results=n_results) print(f"\n検索クエリ: {query}") print("─" * 44) for i, (doc_id, doc, dist) in enumerate(zip( results["ids"][0], results["documents"][0], results["distances"][0] ), 1): print(f"[{i}] {doc_id} (距離: {dist:.4f})") print(f" {doc[:80]}") print() if __name__ == "__main__": q = " ".join(sys.argv[1:]) if len(sys.argv) > 1 else "パスワード管理" search_docs(q)

2. 意味検索を実行する

# 「アクセス認証」でサーバー関連ドキュメントを意味検索する (venv) $ python3 search.py "サーバーへのアクセス認証について" 検索クエリ: サーバーへのアクセス認証について ──────────────────────────────────────────── [1] ssh_policy.txt (距離: 0.2341) 本番サーバーへのSSHは多要素認証を必須とする。鍵認証とOTPを組み合わせる。 [2] security_policy.txt (距離: 0.5123) rootパスワードは月次で変更すること。変更はpasswdコマンドを使う。

クエリに「多要素認証」という言葉が含まれていなくても、意味的に近い ssh_policy.txt が1位に返っています。距離値は小さいほど類似度が高く、`nomic-embed-text` では0.0~0.4が高関連、0.7以上は意味的な関連が薄いことが多いです。

RAGパイプラインとして使う場合は、検索結果のドキュメントをOllamaのChat APIに「コンテキスト」として渡します。ローカルLLMをRAGの回答生成エンジンとして使う際の社内展開判断については社内でChatGPTが使えないときの代替手段の記事も参考にしてください。

ChromaDBのコレクション管理と永続化・データ確認

本番運用に向けて、コレクションの確認・カウント・削除・バックアップの操作を押さえておきます。Kubernetesへのスケールアウトや外部クライアントからの接続が必要になった場合は、`chromadb.HttpClient` を使ったクライアントサーバー構成への移行も検討してください。

1. コレクション一覧とドキュメント数を確認する

(venv) $ python3 -c " import chromadb client = chromadb.PersistentClient(path='/opt/chromadb-data') for col in client.list_collections(): c = client.get_collection(col.name) print(f'{col.name}: {c.count()} 件') " internal_docs: 2 件

2. 格納済みドキュメントの内容を取得して確認する

(venv) $ python3 -c " import chromadb client = chromadb.PersistentClient(path='/opt/chromadb-data') col = client.get_collection('internal_docs') all_docs = col.get() for doc_id, doc in zip(all_docs['ids'], all_docs['documents']): print(f'[{doc_id}] {doc[:60]}') " [security_policy.txt] rootパスワードは月次で変更すること。変更はpasswdコマン [ssh_policy.txt] 本番サーバーへのSSHは多要素認証を必須とする。鍵認証とOTPを組

3. データをバックアップする

ChromaDBの永続化データは `/opt/chromadb-data` ディレクトリ以下のSQLiteファイルです。バックアップはディレクトリごとコピーするだけで済みます。

# ChromaDBのデータをrsyncでバックアップする $ rsync -av /opt/chromadb-data/ /backup/chromadb-data-$(date +%Y%m%d)/ sending incremental file list ./ chroma.sqlite3 2026/09/11 07:00:01 (xfer#1, to-check=0/1) 合計サイズ: 1.2MB (speedup is 1.00)

定期バックアップはcronに登録しておくと安全です。コレクションを完全削除するには `client.delete_collection("internal_docs")` を使います。削除後は格納データもすべて消えるため慎重に実行してください。

よくあるエラーと対処法

構築時に遭遇しやすいエラーを整理します。本番投入前にこの節を確認しておくと、原因調査の時間を短縮できます。

エラー1: "model 'nomic-embed-text' not found"

OllamaのEmbedding APIを叩いたときに発生します。モデルの取得が済んでいないか、モデル名のタイポが原因です。`ollama pull nomic-embed-text` を実行してから再試行してください。取得済みモデル一覧は `ollama list` で確認できます。

エラー2: "Collection already exists"

`create_collection` を重複実行すると発生します。既存コレクションを再利用する場合は `get_or_create_collection` を使ってください。既存コレクションをリセットしたい場合は `delete_collection` で削除してから `create_collection` で作り直します。

エラー3: "Expected embedding with dimension X, got Y"

Embeddingモデルを変更(nomic-embed-text→mxbai-embed-largeなど)すると次元数が変わり、既存コレクションのベクターと不一致が起きます。モデルを切り替えるときは既存コレクションを削除し、新しいモデルで再インジェストする必要があります。コレクション作成時にモデル名をメモしておくと後から混乱しません。

エラー4: httpx.ConnectError(OllamaのAPIに繋がらない)

Dockerコンテナ内でChromaDBを動かしている場合、`http://localhost:11434` ではOllamaに繋がりません。`http://host.docker.internal:11434`(Docker Desktop)または OllamaサーバーのホストIPアドレスに変えてください。Ollamaが特定のネットワークインターフェースのみで待ち受けている場合は `OLLAMA_HOST=0.0.0.0:11434` を設定してから `systemctl restart ollama` で再起動します。

まとめ

ChromaDBとOllamaを組み合わせることで、クラウドを一切使わないローカル意味検索基盤を構築できます。本記事で扱った内容を以下に整理します。

手順 コマンド・ポイント
Embeddingモデルの取得 ollama pull nomic-embed-text
ChromaDBのインストール pip install chromadb httpx
カスタムEmbedding関数 OllamaEmbeddingFunction クラスを実装する
永続化クライアントの初期化 chromadb.PersistentClient(path="/opt/chromadb-data")
ドキュメントのインジェスト collection.add(documents=texts, ids=ids)
意味検索の実行 collection.query(query_texts=[query], n_results=3)
データのバックアップ rsync -av /opt/chromadb-data/ /backup/
この構成はRAGパイプラインの中核ステップです。ChromaDBの検索結果をOllamaのChat APIに「コンテキスト」として渡すことで、社内ドキュメントへの質問応答システムに発展させられます。次のステップとして、チャンキング戦略(1ドキュメントを300~600字に分割してインジェスト)やメタデータフィルタリング(カテゴリ・日付での絞り込み)を組み合わせると、実務で使える精度に近づきます。

20年以上のLinux教育の現場では、ローカルで完結するAI基盤への関心が急速に高まっています。ChromaDBとOllamaの組み合わせは、その入門として最も試しやすい構成の一つです。

ChromaDB+OllamaのベクターDB構築を2日間のハンズオンで体験する

ローカルLLMを使ったベクターDB・RAGパイプラインの実装を、実機GPU環境で手を動かしながら習得したい方向けに、「ローカルAIマスターセミナー」を開催しています。
少人数(最大8名)ZOOMハンズオン形式で実施しています。

>> ローカルAIマスターセミナーの詳細を確認する
ローカルLLMの構築・運用に関する関連記事もあわせて参考にしてください。

Ubuntu ServerでローカルLLMを構築する方法|Ollamaで機密データを外に出さず業務AIを動かす完全ガイド
社内でChatGPTが使えないときの代替手段|機密データを守るローカルLLMという選択肢
ローカルLLMのモデルを比較する方法|Llama3.3・Mistral・Gemma・Phi-4をUbuntuで使い分けるポイント

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。