「社内文書をローカルで意味検索したいが、クラウドのEmbedding APIにデータを送るのは情報漏洩リスクが気になる」
そんな課題を抱えるインフラエンジニアや情シス担当者は多いはずです。この記事では、OllamaのEmbedding APIを使ってテキストをベクトル化し、ローカル環境で意味的な類似検索を実現する手順を解説します。
curlによるAPIの基本的な呼び出し方から、PythonでのEmbedding取得、SQLiteを使った簡易ベクトルDB構築まで、実務で使える形でまとめました。
この記事のポイント
・OllamaのEmbedding APIは/api/embedエンドポイントでテキストをベクトル化できる
・nomic-embed-text(768次元)またはmxbai-embed-large(1024次元)をモデルとして使う
・取得したベクトルとコサイン類似度でキーワード一致なしの意味検索が実現できる
・SQLiteに埋め込みを保存することで数千件規模の社内文書検索DBを低コストで構築できる
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
OllamaのEmbedding APIとは何か|テキスト生成APIとの違い
Ollamaは「テキストを生成するAPI」と「テキストをベクトル化するAPI」の2種類を提供しています。チャット・テキスト生成に使う/api/chat・/api/generateとは別に、Embedding専用のエンドポイント/api/embedがあります。Embedding APIは、入力されたテキストを「数値の配列(ベクトル)」として出力します。このベクトルはテキストの意味を多次元の数値空間で表現したもので、意味が近いテキスト同士は空間的にも近い位置に配置される性質があります。
主な活用場面は3つです。
・意味的な類似検索:キーワードが一致しなくても意味が近い文書を探す
・RAGの前処理:社内ドキュメントをベクトル化して質問応答システムに活用する
・テキスト分類・クラスタリング:問い合わせ内容を自動カテゴリ分けする
クラウドのEmbedding API(OpenAIのtext-embedding-3-smallなど)はトークン数に応じて課金されます。Ollamaをローカルで動かせば、同じ処理をコストゼロで実行できます。機密性の高い社内文書を外部サーバーに送らずに処理できる点も実務上の大きなメリットです。
(Ollamaのサーバー自体のセットアップが済んでいない場合は、まずUbuntu ServerへのローカルLLM構築手順を参照してください)
Embeddingモデルをインストールする手順
テキスト生成用のモデル(Llama3.3など)とEmbedding専用モデルは別物です。Embedding専用モデルはテキストを出力せず、ベクトルを返すことに特化した軽量モデルです。Ollamaで主に使われるEmbeddingモデルは以下の2つです。
・nomic-embed-text:約274MB、768次元。速度重視。日本語を含む多言語テキストに対応
・mxbai-embed-large:約669MB、1024次元。英語テキストの精度が高い
1. nomic-embed-textをpullする
# nomic-embed-text をインストールする $ ollama pull nomic-embed-text pulling manifest pulling 970aa74c0a90... 274 MB / 274 MB ████████████ 100% verifying sha256 digest writing manifest success # インストール済みモデルを確認する $ ollama list NAME ID SIZE MODIFIED nomic-embed-text:latest 0a109f422b47 274 MB 10 seconds ago
ollama pull mxbai-embed-largeでもインストールできます。どちらを選ぶか迷う場合は、まずnomic-embed-textで試し、精度が不十分であればmxbai-embed-largeに切り替える方法が現実的です。
2. Ollamaサービスの起動を確認する
# Ollamaサービスの状態を確認する $ systemctl status ollama ollama.service - Ollama Service Loaded: loaded (/etc/systemd/system/ollama.service; enabled; vendor preset: enabled) Active: active (running) since Sat 2026-07-05 09:00:00 JST; 3h 20min ago Main PID: 1234 (ollama) # APIが応答するか確認する $ curl -s http://localhost:11434/api/version {"version":"0.9.0"}
Active: active (running)であれば、次のステップへ進めます。
curlでEmbedding APIを呼び出す基本手順
OllamaのEmbedding APIは2種類のエンドポイントがあります。Ollama 0.3.0以降では/api/embedが推奨です(旧エンドポイント/api/embeddingsも引き続き動作しますが、新規実装では新しい方を使ってください)。
1. 単一テキストのベクトル化
# /api/embed エンドポイントで単一テキストをベクトル化する $ curl http://localhost:11434/api/embed \ -X POST \ -H "Content-Type: application/json" \ -d '{"model": "nomic-embed-text", "input": "LinuxのcronジョブがPermission deniedで失敗する"}'
{ "model": "nomic-embed-text", "embeddings": [ [0.12341234, -0.56785678, 0.90120012, -0.34563456, 0.78907890, ...] ], "total_duration": 14143917, "load_duration": 1019200, "prompt_eval_count": 12 }
embeddingsフィールドに768次元の浮動小数点配列が入っています。テキストの生成結果は含まれず、数値だけが返ってきます。この数値列がテキストの意味をエンコードしたベクトルです。
2. 複数テキストのバッチ処理
inputフィールドに配列を渡すと、複数のテキストをまとめてベクトル化できます。ドキュメントを一括登録する際に使います。
# 複数テキストを一度にベクトル化する(バッチ処理) $ curl http://localhost:11434/api/embed \ -X POST \ -H "Content-Type: application/json" \ -d '{ "model": "nomic-embed-text", "input": [ "LinuxのcronジョブがPermission deniedで失敗する", "SSH接続がタイムアウトする問題の対処法", "NFSマウントが自動的に切断される原因" ] }'
embeddingsに、送ったテキスト数分のベクトル配列が返ってきます。curlで動作を確認できたら、次はPythonから呼び出す実装に移ります。
PythonでOllamaのEmbeddingを取得する実装手順
curlで動作が確認できたら、Pythonで埋め込みを取得する実装に移ります。2通りの方法があります。1. ollama Pythonライブラリを使う方法(推奨)
# ollama Pythonライブラリとnumpyをインストールする $ pip install ollama numpy
import ollama # 単一テキストのEmbeddingを取得する response = ollama.embed( model='nomic-embed-text', input='LinuxのcronジョブがPermission deniedで失敗する' ) vector = response['embeddings'][0] print(f"次元数: {len(vector)}") print(f"先頭5要素: {vector[:5]}") # 出力例 # 次元数: 768 # 先頭5要素: [0.12341234, -0.56785678, 0.90120012, -0.34563456, 0.78907890]
2. requestsライブラリで直接呼び出す方法
ollama Pythonライブラリが使えない環境や、既存の社内スクリプトにOllamaを組み込む場合はrequestsを使います。import requests def get_embedding(text, model='nomic-embed-text', host='http://localhost:11434'): """テキストをベクトルに変換して返す""" response = requests.post( f"{host}/api/embed", json={"model": model, "input": text} ) response.raise_for_status() return response.json()['embeddings'][0] # 使い方 vec = get_embedding("SSH接続がタイムアウトする問題") print(f"次元数: {len(vec)}") # 768
コサイン類似度でテキストの意味的な近さを計算する方法
ベクトルが取得できたら、次はベクトル間の距離を計算してテキストの類似度を測ります。最もよく使われる指標が「コサイン類似度」です。値が1に近いほど意味が近く、0に近いほど意味が遠いです。1. numpyでコサイン類似度を実装する
import numpy as np import ollama def get_embedding(text, model='nomic-embed-text'): response = ollama.embed(model=model, input=text) return np.array(response['embeddings'][0]) def cosine_similarity(vec_a, vec_b): """コサイン類似度を計算する(値域: -1~1)""" return float(np.dot(vec_a, vec_b) / (np.linalg.norm(vec_a) * np.linalg.norm(vec_b)))
2. 類似文書検索の動作確認
# 検索クエリと候補ドキュメント query = "SSHが途中で切れてしまう" docs = [ "SSH接続が切断される場合はClientAliveIntervalを設定してください", "cronジョブが実行されない場合はPATH環境変数を確認してください", "SSHのキープアライブを設定してセッションを維持する方法", "NFSマウントのタイムアウトエラーを解消する手順", ] query_vec = get_embedding(query) results = [] for doc in docs: doc_vec = get_embedding(doc) score = cosine_similarity(query_vec, doc_vec) results.append((score, doc)) results.sort(reverse=True) for score, text in results: print(f"{score:.4f} {text}")
0.9187 SSHのキープアライブを設定してセッションを維持する方法 0.8834 SSH接続が切断される場合はClientAliveIntervalを設定してください 0.4012 NFSマウントのタイムアウトエラーを解消する手順 0.3756 cronジョブが実行されない場合はPATH環境変数を確認してください
SQLiteにベクトルを保存して社内文書の検索DBを構築する手順
実務では、社内ドキュメントをあらかじめベクトル化して保存しておき、検索時に類似度を計算する形が基本です。ここではSQLiteとPythonで簡易なベクトルDBを構築します。1. ドキュメントのベクトル化と保存
import sqlite3 import json import numpy as np import ollama # DBの初期化 conn = sqlite3.connect('knowledge_base.db') cursor = conn.cursor() cursor.execute(''' CREATE TABLE IF NOT EXISTS documents ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, content TEXT NOT NULL, model TEXT NOT NULL, embedding TEXT NOT NULL ) ''') conn.commit() def store_document(title, content, model='nomic-embed-text'): """ドキュメントをベクトル化してDBに保存する""" response = ollama.embed(model=model, input=content) vec = response['embeddings'][0] cursor.execute( 'INSERT INTO documents (title, content, model, embedding) VALUES (?, ?, ?, ?)', (title, content, model, json.dumps(vec)) ) conn.commit() print(f"保存完了: {title}") # 社内FAQの例 faq_entries = [ ("SSH切断対処", "SSH接続が切断される場合はsshd_configのClientAliveIntervalを30に設定してください"), ("cronPATH問題", "cronジョブが動かない場合はcrontabの先頭でPATH=/usr/local/bin:/usr/binを設定してください"), ("NFSタイムアウト", "NFSマウントがタイムアウトする場合はmountオプションのtimeoとretransを調整してください"), ("Apache起動失敗", "Apacheが起動しない場合はhttpd -tで設定ファイルの構文チェックを実行してください"), ("ディスク容量不足", "ディスクが満杯の場合はdu -sh /* で使用量の多いディレクトリを特定してください"), ] for title, content in faq_entries: store_document(title, content)
2. 類似文書の検索
def search_similar(query, top_k=3, model='nomic-embed-text'): """クエリに意味的に近いドキュメントをスコア順で返す""" response = ollama.embed(model=model, input=query) query_vec = np.array(response['embeddings'][0]) cursor.execute( 'SELECT title, content, embedding FROM documents WHERE model = ?', (model,) ) rows = cursor.fetchall() results = [] for title, content, emb_json in rows: doc_vec = np.array(json.loads(emb_json)) score = float(np.dot(query_vec, doc_vec) / ( np.linalg.norm(query_vec) * np.linalg.norm(doc_vec) )) results.append((score, title, content)) results.sort(reverse=True) return results[:top_k] # 検索テスト results = search_similar("SSHがしばらくすると切れる") for score, title, content in results: print(f"スコア {score:.4f} [{title}]") print(f" {content}") print()
スコア 0.9134 [SSH切断対処] SSH接続が切断される場合はsshd_configのClientAliveIntervalを30に設定してください スコア 0.5201 [NFSタイムアウト] NFSマウントがタイムアウトする場合はmountオプションのtimeoとretransを調整してください スコア 0.4103 [cronPATH問題] cronジョブが動かない場合はcrontabの先頭でPATH=/usr/local/bin:/usr/binを設定してください
本番運用で注意すべきトラブルと対処法
Embedding APIを実運用に組み込む際、現場でよく遭遇する問題と対処法をまとめます。モデルの初回ロードによるレイテンシ問題
Ollamaはモデルを初回呼び出し時にメモリにロードします。nomic-embed-textは軽量ですが、初回リクエストだけ1秒以上かかる場合があります。サービス起動後にwarm-upリクエストを1回送っておくことで、実際のリクエストを安定して処理できます。# サービス起動後のウォームアップリクエスト例 $ curl -s http://localhost:11434/api/embed \ -X POST \ -H "Content-Type: application/json" \ -d '{"model": "nomic-embed-text", "input": "warmup"}' > /dev/null \ && echo "ウォームアップ完了"
バッチサイズが大きすぎる場合のタイムアウト
一度に送るテキスト数が多すぎると処理が遅くなり、クライアント側のタイムアウトに引っかかる場合があります。1バッチあたりのテキスト数を10件程度に分割して送る実装が安全です。def embed_in_batches(texts, batch_size=10, model='nomic-embed-text'): """テキストリストをバッチ分割してEmbeddingを取得する""" all_embeddings = [] for i in range(0, len(texts), batch_size): batch = texts[i:i + batch_size] response = ollama.embed(model=model, input=batch) all_embeddings.extend(response['embeddings']) print(f"進捗: {min(i + batch_size, len(texts))}/{len(texts)}件") return all_embeddings # 使い方(100件のテキストを10件ずつ処理) embeddings = embed_in_batches(large_text_list) print(f"取得完了: {len(embeddings)}件")
モデルの次元数不一致によるエラー
nomic-embed-text(768次元)とmxbai-embed-large(1024次元)のベクトルを同じDBに混在させると、コサイン類似度の計算で次元数不一致エラーが発生します。DBのmodel列を必ず記録し、検索時は同一モデルのベクトルだけを対象にすることが鉄則です。モデルを切り替える場合はDBの全データを再ベクトル化してください。どのEmbeddingモデルを選ぶかは、まず処理速度と精度のトレードオフで決めます。日本語テキストが多い社内環境ではnomic-embed-textから試すことを推奨します。モデル選定の詳細はローカルLLMのモデル比較手順も参考にしてください。
クラウドAIの利用制限がある環境でのローカルLLM全体の導入判断については、社内ChatGPT禁止時のローカルLLM活用も合わせて読むと判断軸が整います。
本記事のまとめ
OllamaのEmbedding APIを使うと、テキストをベクトル化してローカル環境で意味的な類似検索を実現できます。クラウドAPIへのデータ送信が不要なため、機密情報を含む社内文書の処理に適しています。| やりたいこと | コマンド・方法 |
|---|---|
| Embeddingモデルをインストールする | ollama pull nomic-embed-text |
| curlでベクトルを取得する | curl http://localhost:11434/api/embed -X POST -H "Content-Type: application/json" -d '{"model":"nomic-embed-text","input":"テキスト"}' |
| Pythonでベクトルを取得する | ollama.embed(model='nomic-embed-text', input='テキスト') |
| コサイン類似度を計算する | np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)) |
| SQLiteにベクトルを保存する | json.dumps(vec)でTEXT型カラムに保存する |
| nomic-embed-textの次元数 | 768次元 |
| mxbai-embed-largeの次元数 | 1024次元 |
ローカルEmbeddingと類似検索を2日間のハンズオンで体験する
OllamaのEmbedding APIやRAGシステムの構築を、設定から動作確認まで手を動かして習得したい方向けに、「ローカルAIマスターセミナー」を開催しています。
少人数(最大8名)ZOOMハンズオン形式で実施しています。
・Ubuntu ServerでローカルLLMを構築する方法|Ollamaで機密データを外に出さず業務AIを動かす完全ガイド
・社内でChatGPTが使えないときの代替手段|機密データを守るローカルLLMという選択肢
・ローカルLLMのモデルを比較する方法|Llama3.3・Mistral・Gemma・Phi-4をUbuntuで使い分けるポイント
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
登録10秒/合わなければ解除3秒 / 詳細はこちら
- 前のページへ:OllamaをPrometheus+Grafanaで監視する方法|GPU使用率・レスポンス時間・エラー率をリアルタイムダッシュボードで可視化する
- この記事の属するカテゴリ:ローカルLLMへ戻る

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