「ChatGPTに社内文書を貼り付けて質問したいが、機密情報が外部に送信されるのが不安で踏み切れない」
そんな悩みを抱えるインフラエンジニアや情シス担当者は多いはずです。この記事では、OllamaとChromaDB・LangChainを組み合わせて、社内ドキュメントに対してローカルLLMで質問応答できるRAGシステムをUbuntu Server上に構築する手順を解説します。
外部APIに一切データを送らないため、機密文書を扱う現場でも安全に導入できます。Ollamaのエンベディングモデルでドキュメントをベクター化してChromaDBに保存し、PythonでRAGパイプラインを実装するところまで、順を追って説明します。
この記事のポイント
・ollama pull nomic-embed-textでエンベディングモデルを取得してローカルベクター化を実現する
・LangChainのChromaVectorstoreとOllamaLLMを組み合わせてRAGパイプラインをPythonで実装できる
・ChromaDBはローカルファイルとして動作するため外部サーバー不要で機密文書を安全に扱える
・回答精度が低いときはchunk_sizeとchunk_overlapの調整が最初の改善ポイントになる
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
RAGとは何か|なぜローカルLLMと組み合わせるのか
RAG(Retrieval-Augmented Generation)は、LLMに質問するとき、あらかじめ用意したドキュメントの中から関連する文章を検索して一緒に渡す仕組みです。LLMが学習データだけで回答するのではなく、「今このドキュメントに書いてあること」を根拠として回答させられます。学習カットオフより新しい情報や、社内固有の情報を扱う場合に特に有効です。
知人のインフラエンジニアが社内FAQシステムをRAGで構築した事例では、新入社員からの問い合わせ対応工数が月30時間以上削減できたと聞いています。規則文書・手順書・議事録といった「知っている人でないと答えられなかった」情報を、誰でも引き出せる形にするのがRAGの実際の価値です。
なぜローカルLLMと組み合わせるのか。答えはシンプルで、データが外に出ないからです。
クラウドのRAGサービス(AzureやAWSのマネージドRAG等)は高速ですが、文書の内容がベクター化の段階で外部サーバーを経由します。社内の契約書・仕様書・個人情報を含む議事録を扱う場合、このプロセス自体がリスクになります。
Ollamaはエンベディングモデルも内包しているため、文書のベクター化から質問応答まで、すべてをローカルで完結させられます。情シス部門から「機密情報は外に出すな」という制約がある環境でも、RAGを使ったAI活用が現実のものになります。
社内でAI活用の検討が進む背景については、社内でChatGPTが使えないときの代替手段|機密データを守るローカルLLMという選択肢も参照してください。
構築に必要な環境とツールを確認する
今回のRAGシステムは以下の構成で動作します。事前に確認しておいてください。 ・OS: Ubuntu Server 22.04 LTS または Ubuntu 24.04 LTS・Ollama: v0.3以降(systemdサービスとして起動済みであること)
・Python: 3.10以降(python3 --versionで確認)
・主要ライブラリ: langchain-community、chromadb、pypdf
・ストレージ: ベクターDB保存先に1GB以上の空き(文書量による)
Ollamaがすでにインストールされていることが前提です。まだ導入していない場合は、Ubuntu ServerでローカルLLMを構築する方法|Ollamaで機密データを外に出さず業務AIを動かす完全ガイドを先に参照してください。
Pythonの仮想環境を作成して、必要なライブラリをインストールします。グローバル環境に直接インストールするとバージョン競合が起きやすいため、仮想環境を使うことを推奨します。
# 仮想環境を作成して有効化 $ python3 -m venv ~/rag-env $ source ~/rag-env/bin/activate # 必要ライブラリをまとめてインストール (rag-env) $ pip install langchain-community chromadb pypdf sentence-transformers # インストール確認 (rag-env) $ pip show chromadb | grep Version Version: 0.5.23
OllamaでエンベディングモデルをPullする
RAGでは「質問文とドキュメントを数値ベクターに変換するエンベディング処理」が必要です。Ollamaは言語生成モデルだけでなく、エンベディング専用モデルも提供しています。テキストをベクター化する作業とLLMによる回答生成は別のモデルが担当するため、用途に合ったモデルをそれぞれ選ぶことが重要です。
今回使うエンベディングモデルは `nomic-embed-text` です。274MBと軽量でありながら日本語テキストにも対応しており、RAG用途で広く採用されています。
1. nomic-embed-textをPullする
# エンベディングモデルをPull $ ollama pull nomic-embed-text pulling manifest pulling 970aa74c0a90... 100% ▕████████████████▏ 274 MB pulling c71d239df917... 100% ▕████████████████▏ 11 KB verifying sha256 digest writing manifest success
2. APIを叩いてベクターが返るか確認する
PullできたらREST APIでエンベディングをテストします。正常に動いていれば数値の配列が返ります。# エンベディングAPIの動作確認 $ curl -s http://localhost:11434/api/embeddings \ -d '{"model":"nomic-embed-text","prompt":"ローカルLLMテスト"}' \ | python3 -c "import sys,json; d=json.load(sys.stdin); print(len(d['embedding']), 'dims')" 768 dims
回答生成に使うLLMは、目的に合わせて `llama3.3:70b-instruct-q4_0` や `mistral:7b-instruct-q4_0` を選べます。モデルの特性と使い分けについては、ローカルLLMのモデルを比較する方法|Llama3.3・Mistral・Gemma・Phi-4をUbuntuで使い分けるポイントを参照してください。
社内ドキュメントをチャンク分割してChromaDBにベクター保存する
RAGの精度を大きく左右するのがチャンク分割の設定です。長い文書をそのままベクター化するのではなく、一定の長さのブロック(チャンク)に切り分けてからベクター化し、ChromaDBに保存します。チャンクが大きすぎると関係ない情報が混入して回答がぼやけ、小さすぎると文脈が失われて意図を掴めない回答になります。日本語テキストでは500~800文字を目安にするのが一般的です。ここでは `chunk_size=500`・`chunk_overlap=50` から始めて、動作確認後に調整します。
1. docsフォルダにPDFを配置する
# 作業ディレクトリを作成してPDFを配置 $ mkdir -p ~/rag-project/docs $ cp /path/to/your/manual.pdf ~/rag-project/docs/company_manual.pdf $ ls ~/rag-project/docs/ company_manual.pdf
2. ingest.pyを作成して実行する
以下のPythonスクリプトを `~/rag-project/ingest.py` として保存します。PDFの読み込み・チャンク分割・ChromaDBへの保存をまとめて処理します。from langchain_community.document_loaders import PyPDFLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.vectorstores import Chroma from langchain_community.embeddings import OllamaEmbeddings # PDFを読み込む loader = PyPDFLoader("./docs/company_manual.pdf") documents = loader.load() print(f"読み込みページ数: {len(documents)}") # チャンク分割の設定 splitter = RecursiveCharacterTextSplitter( chunk_size=500, chunk_overlap=50, separators=["\n\n", "\n", "。", "、", " "] ) chunks = splitter.split_documents(documents) print(f"チャンク数: {len(chunks)}") # OllamaのエンベディングでChromaDBに保存(persist_directoryで自動永続化) embeddings = OllamaEmbeddings(model="nomic-embed-text") vectorstore = Chroma.from_documents( documents=chunks, embedding=embeddings, persist_directory="./chroma_db" ) print("ChromaDBへの保存が完了しました")
# 仮想環境を有効化してから実行 $ source ~/rag-env/bin/activate (rag-env) $ cd ~/rag-project (rag-env) $ python3 ingest.py 読み込みページ数: 42 チャンク数: 187 ChromaDBへの保存が完了しました
PDFではなくTXTやMarkdownを読み込みたい場合は、`PyPDFLoader` の代わりに `TextLoader` を使います。複数ファイルを一括処理する場合は `DirectoryLoader` が便利です。スキャンPDF(画像PDF)はテキストが抽出できないため、後述のトラブル対処を参照してください。
PythonでRAGパイプラインを実装して質問応答する
ChromaDBへの保存が完了したら、質問応答のパイプラインを実装します。「質問を受け取る → 関連チャンクをベクター検索する → LLMに渡して回答を生成する」という流れをPythonで組みます。1. rag_query.pyを作成する
from langchain_community.vectorstores import Chroma from langchain_community.embeddings import OllamaEmbeddings from langchain_community.llms import Ollama from langchain.chains import RetrievalQA from langchain.prompts import PromptTemplate # 保存済みChromaDBを読み込む embeddings = OllamaEmbeddings(model="nomic-embed-text") vectorstore = Chroma( persist_directory="./chroma_db", embedding_function=embeddings ) # 回答生成モデルを指定 llm = Ollama(model="mistral:7b-instruct-q4_0") # 日本語で回答するようにプロンプトを設定 prompt_template = """以下のコンテキストを参照して、質問に必ず日本語で簡潔に答えてください。 コンテキストに答えが見当たらない場合は「この資料には記載がありません」とだけ答えてください。 コンテキスト: {context} 質問: {question} 回答:""" PROMPT = PromptTemplate( template=prompt_template, input_variables=["context", "question"] ) # RAGチェーンを構築(k=4で上位4チャンクを参照) qa_chain = RetrievalQA.from_chain_type( llm=llm, chain_type="stuff", retriever=vectorstore.as_retriever(search_kwargs={"k": 4}), chain_type_kwargs={"prompt": PROMPT} ) print("ローカルRAGシステム起動(終了: Ctrl+C)") while True: question = input("\n質問を入力: ") if not question.strip(): continue result = qa_chain({"query": question}) print("\n回答:", result["result"])
2. 実行して動作を確認する
(rag-env) $ python3 rag_query.py ローカルRAGシステム起動(終了: Ctrl+C) 質問を入力: 有給休暇の申請方法を教えてください 回答: 有給休暇の申請は就業規則第12条に基づき、取得希望日の3営業日前までに 人事システムから申請します。緊急の場合は直属の上長に口頭で相談のうえ、 事後申請が可能です。 質問を入力: サーバーのパスワードポリシーはどうなっていますか 回答: パスワードは12文字以上、英数字・記号を含む組み合わせが必須です。 90日ごとの変更が義務付けられており、直近5世代との重複は不可です。
`search_kwargs={"k": 4}` の数値を増やすと参照チャンク数が増えて文脈が広くなりますが、LLMへ渡すトークン数も増えるため応答速度が落ちます。4~6の範囲で調整するのが現実的です。
RAGでよく起きる問題と対処法
実際にRAGを動かし始めると、いくつかの典型的なトラブルに遭遇します。対処法をまとめます。回答が的外れ・「記載がありません」ばかり返る
チャンクサイズが小さすぎて文脈が分断されている可能性があります。
ingest.py の chunk_size を500→800に増やし、chunk_overlap も50→100程度に広げてから chroma_db フォルダを削除して再インジェストしてください。検索ヒット数 k を4→6に増やすことも効果的です。日本語の質問なのに英語で回答が返る
プロンプトテンプレートの冒頭に「必ず日本語で回答してください」と明示しても改善しない場合は、使用モデルを見直します。Llama3.3やMistral・Gemma 3はいずれも日本語に対応していますが、量子化レベル
-q4_0 では日本語精度が下がることがあります。-q8_0 タグのモデルを試すと改善するケースがあります。chromadb のインポートエラーが出るlangchain-community と chromadb のバージョンの組み合わせによってAPIが変わることがあります。まずは以下でアップデートしてください。
# ライブラリを最新版に更新 (rag-env) $ pip install -U langchain-community chromadb # バージョン確認 (rag-env) $ pip show langchain-community chromadb | grep -E "^Name|^Version" Name: langchain-community Version: 0.3.23 Name: chromadb Version: 0.5.23
スキャンPDF(画像PDF)のテキストが抽出できない
pypdf はテキストレイヤーが埋め込まれたPDFのみ対応しています。印刷物をスキャンしたPDFはOCRツール(tesseract-ocr)で先にテキスト化し、TXTファイルとして TextLoader で読み込む必要があります。応答が遅くCPU使用率が100%になる
GPUなし環境ではCPUで推論するため、回答生成に20秒以上かかることがあります。軽量モデル(
phi-4:14b-q4_0 等)に切り替えるか、NVIDIA GPUを搭載したサーバーにOllamaを移行するとレスポンスが大幅に改善します。
本記事のまとめ
OllamaとChromaDB・LangChainを組み合わせたRAGシステムの構築手順を解説しました。すべてローカル環境で完結するため、機密性の高い社内ドキュメントを扱う現場でも安全に導入できます。| 手順 | コマンド・ポイント |
|---|---|
| エンベディングモデル取得 | ollama pull nomic-embed-text |
| ライブラリのインストール | pip install langchain-community chromadb pypdf |
| ドキュメントのベクター保存 | python3 ingest.py(./chroma_db/に保存される) |
| RAGパイプラインの起動 | python3 rag_query.py(対話形式で質問応答) |
| 精度が低いときの改善 | chunk_size・chunk_overlapを調整して再インジェスト |
今回の構成はシンプルな「1台完結型」です。ドキュメント数が増えてきた場合は、定期的に `ingest.py` を実行してベクターDBを更新するcronジョブを組み込むと運用が楽になります。
チームで使える形にするには、Open WebUIをフロントエンドとして組み合わせる構成も有効です。GUIからRAGに対応した質問応答を利用できるようになります。
20年以上のサーバー運用経験から言えば、RAGは「知識の属人化」を解消する最も実用的なAI活用の入口です。まずは1つのドキュメントで試し、精度を確かめてから対象を広げていく進め方が、現場での定着につながります。
RAG実装を2日間のハンズオンで体験する
OllamaとChromaDBを使ったローカル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をDockerコンテナで運用する方法|docker-compose.ymlとGPU設定で本番環境に安定デプロイする
- 前のページへ:OllamaのModelfileでカスタムモデルを作る方法|業務特化AIアシスタントをチームで使い回す設定手順
- この記事の属するカテゴリ:ローカルLLMへ戻る

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