mcpRAG について
mcpRAGは、ローカルのOllamaによる埋め込み、FAISS検索、Geminiによる生成を組み合わせた文書QAパイプラインです。テキストファイルを索引化して質問し、文書を追加したいユーザー向けです。
主な機能
- 01Ollamaで埋め込みを生成
- 02FAISSでベクトルを索引化
- 03Geminiで質問に回答
- 04索引を再構築せず文書を追加
- 05チャンクのメタデータをローカル保存
活用シーン
- 01TXT文書群への質問
- 02SAP文書のRAGシステム構築
- 03既存の索引へのファイル追加
MCP 対応
エージェント用途のMCPサーバーと説明されていますが、READMEにはMCPツールやインターフェースの詳細がありません。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
このサーバーのパッケージやリモートエンドポイントはまだ取得できていません。プロジェクトの README の手順に従ってください。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
mcpRAG - 検索拡張生成システム
オープンソースの埋め込みモデル、FAISS ベクトルデータベース、Gemini LLM を組み合わせた、本番環境対応の RAG(検索拡張生成)実装です。ドキュメントに基づく正確な質疑応答を実現します。
概要
このプロジェクトでは、次の技術を使用してテキストドキュメントを検索する完全な RAG パイプラインを実装します。
- 埋め込み:Ollama 経由の Nomic 埋め込み(ローカル、オープンソース)
- ベクトルデータベース:FAISS(Facebook AI Similarity Search)
- LLM:Gemini 2.0 Flash(高速で費用対効果に優れています)
- ドキュメント処理:コンテキストを保持するため、重複部分を含めてチャンクに分割します
- 増分更新:インデックス全体を再構築せずに、新しいドキュメントを追加できます
アーキテクチャ
Documents (TXT) → Chunking (60 words, 10-word overlap)
↓
Nomic Embeddings (Ollama, 768 dimensions)
↓
FAISS Vector Index (L2 distance similarity)
↓
User Query → Query Embedding → FAISS Search → Top-K Chunks
↓
Context + Query → Gemini LLM
↓
Answer
主な機能
- ローカル埋め込み:Nomic モデルと Ollama を使用するため、埋め込みに外部 API 呼び出しは不要です
- 効率的な検索:FAISS IndexFlatL2 による高速な類似検索
- チャンク分割戦略:コンテキストを保持するため、60 語のチャンクに 10 語の重複を設けます
- 増分更新:
appendEmbeds.pyで、インデックス全体を再構築せずに新しいドキュメントを追加できます - メタデータ追跡:追跡可能性を確保するため、ドキュメント名、チャンク ID、チャンクテキストを保存します
- 永続ストレージ:再利用できるよう、インデックスとメタデータをローカルに保存します
インストール
前提条件
- Python 3.11+
- Ollama がインストールされ、ローカルで実行されていること
- Gemini API キー
- Ollama に Nomic 埋め込みモデルがダウンロードされていること
セットアップ
- Ollama をインストールします(まだインストールされていない場合):
# macOS/Linux
curl -fsSL https://ollama.ai/install.sh | sh
# Windows
# Download from https://ollama.ai/download
- Nomic 埋め込みモデルをプルします:
ollama pull nomic-embed-text
Ollama が実行中であることを確認します。
curl http://localhost:11434/api/embeddings -d '{"model":"nomic-embed-text","prompt":"test"}'
- プロジェクトをクローンして、ディレクトリに移動します:
cd mcpRAG
- 仮想環境を作成して、依存関係をインストールします:
python -m venv .venv
# Windows
.venv\Scripts\activate
# macOS/Linux
source .venv/bin/activate
pip install -r requirements.txt
- API 認証情報を設定します。
.env ファイルを作成します。
GEMINI_API_KEY=your_gemini_api_key_here
API キーは Google AI Studio から取得してください。
- ドキュメントを準備します。
.txt ファイルを docfiles/ または sapfiles/ ディレクトリに配置します。
使用方法
1. 初期の埋め込みとインデックスを作成する
ollamaEmbed.py を実行して、最初のドキュメントを処理し、FAISS インデックスを作成します。
# Edit ollamaEmbed.py to set your document path
file_path = Path("C:\\path\\to\\your\\document.txt")
# Then run
python ollamaEmbed.py
処理内容:
- 指定されたテキストファイルを読み込みます
- テキストを 60 語のセグメントに分割し、10 語の重複を設けます
- Ollama の Nomic モデルを使用して埋め込みを生成します
- L2 距離を指標とする FAISS インデックスを作成します
- インデックスを
my_faiss_index.faissに保存します - メタデータを
my_metadata.jsonに保存します
出力:
Processing document.txt...
✅ Indexed 142 chunks
✅ Saved FAISS index to my_faiss_index.faiss
✅ Saved metadata to my_metadata.json
2. RAG システムにクエリを送信する
ragModel.py を実行して、インデックスを検索し、回答を生成します。
# Edit ragModel.py to set your query
query = "how does SAP integrate with other systems?"
top_k = 5 # Number of relevant chunks to retrieve
# Then run
python ragModel.py
処理内容:
- FAISS インデックスとメタデータを読み込みます
- Nomic を使用してクエリを埋め込みに変換します
- FAISS を検索し、類似度が最も高い上位 K 個のチャンクを取得します
- 取得したコンテキストとクエリを Gemini 2.0 Flash に渡します
- コンテキストに基づいた回答を返します
出力例:
Query: how does SAP integrate with other systems?
Result 1:
Document: SAP_Joule.txt
Chunk: SAP integrates with various external systems through APIs...
Chunk ID: SAP_Joule_42
--------------------
Gemini Response: SAP integrates with other systems primarily through REST APIs
and OData services. The integration layer supports real-time data synchronization...
3. 既存のインデックスに新しいドキュメントを追加する
appendEmbeds.py を実行すると、インデックスを再構築せずにドキュメントを段階的に追加できます。
# Edit appendEmbeds.py to set your new document path
new_file_path = "C:\\path\\to\\new_document.txt"
# Then run
python appendEmbeds.py
処理内容:
- 既存の FAISS インデックスとメタデータを読み込みます
- 新しいドキュメントを処理します(チャンク分割と埋め込み生成)
- 新しい埋め込みを既存の FAISS インデックスに追加します
- 新しいチャンクをメタデータに反映します
- 更新したインデックスとメタデータを保存します
出力:
Processing SAP_GENAI.txt...
✅ Added 87 new chunks to the index
✅ Saved updated FAISS index to my_faiss_index.faiss
✅ Saved updated metadata to my_metadata.json
プロジェクトファイル
| ファイル | 用途 |
|---|---|
ollamaEmbed.py | 初期ドキュメントの処理、埋め込みの生成、FAISS インデックスの作成 |
ragModel.py | クエリインターフェース。インデックスを検索し、Gemini で回答を生成します |
appendEmbeds.py | 既存のインデックスに新しいドキュメントを段階的に追加します |
requirements.txt | Python の依存関係 |
.env | API 認証情報(このファイルを作成してください) |
my_faiss_index.faiss | 保存された FAISS ベクトルインデックス(自動生成) |
my_metadata.json | ドキュメント名とテキストを含むチャンクメタデータ(自動生成) |
docfiles/ | ソースドキュメント用ディレクトリ |
sapfiles/ | SAP 関連ドキュメント用ディレクトリ |
設定
チャンク分割のパラメーター
Python ファイルのいずれかで調整します。
CHUNK_SIZE = 60 # Number of words per chunk
CHUNK_OVERLAP = 10 # Overlapping words between chunks
推奨設定:
- 小さめのチャンク(30〜50語):精度が高く、チャンク数が増え、インデックス作成が遅くなります
- 大きめのチャンク(80〜100語):コンテキストが増え、チャンク数が減り、インデックス作成が速くなります
- オーバーラップ(10〜20語):チャンク境界でのコンテキストの欠落を防ぎます
検索設定
ragModel.py で top_k を調整します。
top_k = 5 # Number of chunks to retrieve
推奨設定:
- top_k=3:高速で、焦点を絞った回答
- top_k=5:バランスの取れた設定(デフォルト)
- top_k=10:より包括的なコンテキストが得られますが、処理が遅くなります
LLM モデル
ragModel.py で Gemini モデルを変更します。
model="gemini-2.0-flash" # Fast, cost-effective (default)
model="gemini-1.5-pro" # More capable, higher cost
model="gemini-1.5-flash" # Balanced
埋め込みモデル
Ollama の埋め込みモデルを変更します(モデルのプルが必要です)。
"model": "nomic-embed-text" # 768 dimensions (default)
"model": "mxbai-embed-large" # 1024 dimensions
モデルを変更する場合は、既存の .faiss ファイルと .json ファイルを削除して、再構築してください。
仕組み
1. ドキュメントのチャンク分割
テキストはオーバーラップするチャンクに分割されます。
Original: "The quick brown fox jumps over the lazy dog..."
Chunk 1: "The quick brown fox jumps..." (words 0-60)
Chunk 2: "...fox jumps over the lazy..." (words 50-110, 10-word overlap)
Chunk 3: "...lazy dog runs through..." (words 100-160, 10-word overlap)
2. 埋め込みの生成
各チャンクは Ollama によって 768 次元のベクトルに変換されます。
response = requests.post(
"http://localhost:11434/api/embeddings",
json={"model": "nomic-embed-text", "prompt": chunk}
)
embedding = np.array(response.json()["embedding"]) # Shape: (768,)
3. FAISS によるインデックス作成
埋め込みは FAISS IndexFlatL2(L2 距離)に保存されます。
dimension = 768
index = faiss.IndexFlatL2(dimension)
index.add(np.stack(all_chunks)) # Add all embeddings
FAISS IndexFlatL2 はユークリッド距離を使用します。
- ベクトル間の距離が近いほど(L2 距離が小さいほど)、類似性が高くなります
- 総当たり検索により、最適な一致が保証されます
- 約 10 万個までのベクトルを高速に処理できます
4. セマンティック検索
クエリを埋め込み、インデックスを検索します。
query_embedding = get_embedding(query) # Shape: (768,)
D, I = index.search(query_embedding.reshape(1, -1), top_k=5)
# D: distances, I: indices of most similar chunks
5. 回答の生成
取得したチャンクをコンテキストとして Gemini に渡します。
context = "\n".join([metadata[i]["chunk"] for i in I[0]])
prompt = f"Query: {query}\n\nContext: {context}\n\nAnswer:"
response = client.models.generate_content(model="gemini-2.0-flash", contents=prompt)
ワークフローの例
シナリオ:SAP ドキュメントの RAG システムを構築する
- SAP Joule のドキュメントを使った初期セットアップ:
# Edit ollamaEmbed.py
file_path = Path("sapfiles/SAP_Joule.txt")
python ollamaEmbed.py
# Output: ✅ Indexed 142 chunks
- システムにクエリを送信します。
# Edit ragModel.py
query = "What is SAP Joule?"
python ragModel.py
# Output: SAP Joule is an AI assistant that helps users...
- ドキュメントを追加します。
# Edit appendEmbeds.py
new_file_path = "sapfiles/SAP_GENAI.txt"
python appendEmbeds.py
# Output: ✅ Added 87 new chunks to the index
- ナレッジベースを拡充してクエリを送信します。
# Query now searches across both documents
python ragModel.py
高度な使い方
複数のドキュメントをバッチ処理する
ollamaEmbed.py を変更し、ディレクトリを処理するようにします。
from pathlib import Path
all_chunks = []
metadata = []
for file_path in Path("docfiles").glob("*.txt"):
chunks, meta = process_document(file_path)
all_chunks.extend(chunks)
metadata.extend(meta)
# Then create index as usual
カスタム類似度メトリクス
IndexFlatL2 をほかの FAISS インデックスに置き換えます。
# Inner product (for normalized embeddings)
index = faiss.IndexFlatIP(dimension)
# Approximate search (faster for large datasets)
index = faiss.IndexIVFFlat(quantizer, dimension, nlist=100)
マルチクエリ RAG
調整したプロンプトで複数回クエリを実行します。
queries = [
"What is SAP Joule?",
"How does SAP Joule integrate with SAP systems?",
"What are the key features of SAP Joule?"
]
for query in queries:
results = search_index(index, metadata, query, top_k=5)
# Process results...
パフォーマンスの最適化
大規模なドキュメントコレクション(1 万チャンク超)の場合
- IVFPQ インデックス(積量子化を用いた転置ファイル)を使用します。
nlist = 100 # Number of clusters
m = 8 # Number of subquantizers
quantizer = faiss.IndexFlatL2(dimension)
index = faiss.IndexIVFPQ(quantizer, dimension, nlist, m, 8)
index.train(embeddings) # Training required
index.add(embeddings)
- 埋め込みの生成をバッチ処理します。
# Process chunks in batches
batch_size = 50
for i in range(0, len(chunks), batch_size):
batch = chunks[i:i+batch_size]
# Generate embeddings for batch...
クエリを高速化するには
- top_k を小さくする:取得するチャンク数を減らします(5 個ではなく 3 個)
- Gemini Flash を使用する:Pro モデルより高速です
- 埋め込みをキャッシュする:繰り返し実行するクエリの埋め込みを保存します
トラブルシューティング
Ollama の接続エラー
エラー:requests.exceptions.ConnectionError: Failed to establish a new connection
解決方法:
# Check if Ollama is running
curl http://localhost:11434/api/tags
# If not, start Ollama
ollama serve
モデルが見つからない
エラー:model 'nomic-embed-text' not found
解決方法:
ollama pull nomic-embed-text
ollama list # Verify model is downloaded
FAISS の次元数の不一致
エラー:RuntimeError: Error in void faiss::IndexFlat::add_with_ids()
解決方法:既存の .faiss ファイルと .json ファイルを削除して、再構築します。
rm my_faiss_index.faiss my_metadata.json
python ollamaEmbed.py
Gemini API エラー
エラー:google.api_core.exceptions.PermissionDenied: 403 API key not valid
解決方法:.env ファイルを確認します。
cat .env # Should show: GEMINI_API_KEY=AIza...
Google AI Studio から新しいキーを取得してください。
メモリの問題(大きなドキュメント)
エラー:MemoryError: Unable to allocate array
解決方法:ドキュメントをより小さなバッチで処理するか、次元削減を使用します。
# Use PCA to reduce dimensions
import faiss
pca_matrix = faiss.PCAMatrix(768, 256) # Reduce to 256 dimensions
index = faiss.IndexFlatL2(256)
技術的な詳細
FAISS IndexFlatL2
- アルゴリズム:総当たり検索
- メトリクス:L2(ユークリッド)距離
- 精度:100%(最近傍を正確に検索します)
- 速度:O(N*D)。N はベクトル数、D は次元数です
- 最適な用途:768 次元のベクトルを 100 万個まで扱う場合
Nomic Embeddings
- モデル:
nomic-embed-text - 次元数:768
- コンテキスト長:8192 トークン
- 学習データ:セマンティック類似性のための数十億のテキストペア
- ライセンス:オープンソース(Apache 2.0)
チャンク分割戦略
なぜ60語で10語重複させるのですか?
- 60語:およそ300〜400文字で、一般的な意味単位(段落)に適しています
- 10語の重複:境界でのコンテキストの喪失を防ぎます
- 結果:各チャンクに隣接するチャンクのコンテキストが少し含まれます
トレードオフ:
- 重複を増やす → コンテキストは向上し、ストレージは増加します
- 重複を減らす → 処理は速くなりますが、コンテキストに抜けが生じる可能性があります
代替手段との比較
| コンポーネント | このプロジェクト | 代替手段 |
|---|---|---|
| 埋め込み | Ollama(ローカル、無料) | OpenAI($0.0001/1Kトークン) |
| ベクトルDB | FAISS(ローカル、高速) | Pinecone/Weaviate(クラウド、$$) |
| LLM | Gemini Flash(低コスト) | GPT-4(高コスト) |
| ストレージ | ローカルファイル | クラウドデータベース |
| プライバシー | 埋め込みはローカルに保存 | データはAPIに送信 |
利点:
- 低コスト(LLM API呼び出しのみ)
- プライバシー(埋め込みはローカルに保持)
- 高速(ローカルのFAISS検索)
- ベンダーロックインなし
欠点:
- Ollamaのセットアップが必要
- ローカルストレージが必要
- スケーリング機能が組み込まれていない
プロジェクトの拡張
PDFドキュメントのサポートを追加する
pip install PyPDF2
import PyPDF2
def extract_text_from_pdf(pdf_path):
with open(pdf_path, 'rb') as file:
reader = PyPDF2.PdfReader(file)
text = ""
for page in reader.pages:
text += page.extract_text()
return text
会話メモリを追加する
会話履歴を保存し、コンテキストに含めます:
conversation_history = []
conversation_history.append({"role": "user", "content": query})
conversation_history.append({"role": "assistant", "content": response.text})
StreamlitでWeb UIを追加する
pip install streamlit
import streamlit as st
st.title("RAG Document Q&A")
query = st.text_input("Ask a question:")
if st.button("Search"):
results = search_index(index, metadata, query, top_k=5)
answer = generate_answer(results, query)
st.write(answer)
MCP サーバーとしてデプロイする
MCP(Model Context Protocol)のパターンと統合します:
- RAG検索をMCPツールとして公開します
- ドキュメント管理用のリソースを追加します
- 参考実装については、
../agenticMCP_rag/を参照してください
参考資料
ライセンス
実験的なAI/MLモノレポの一部です。ライセンスの詳細については、ルートリポジトリを参照してください。
関連プロジェクト
より高度なRAG実装については、以下を参照してください:
../agenticMCP_rag/- MCP統合とエージェントアーキテクチャを備えたRAG../R_RAG/- 代替のRAG実装../perceptionAgent/- 知覚・意思決定・行動パターンを備えたエージェントアーキテクチャ
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。