关于 mcpRAG
mcpRAG 是一个文档问答流程,结合本地 Ollama 嵌入、FAISS 检索和 Gemini 生成。适用于希望索引文本文件、查询内容并逐步添加文档的用户。
主要功能
- 01使用 Ollama 生成嵌入
- 02使用 FAISS 建立向量索引
- 03通过 Gemini 回答问题
- 04增量添加文档而无需重建索引
- 05在本地保存分块元数据
适用场景
- 01查询一组 TXT 文档
- 02为 SAP 文档构建 RAG 系统
- 03向现有索引添加新文件
MCP 相关
项目说明其 MCP 服务用于 Agent 场景,但提供的 README 未说明 MCP 工具或接口。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
mcpRAG - 检索增强生成系统
一个可用于生产环境的 RAG(检索增强生成)实现,结合了开源嵌入、FAISS 向量数据库和 Gemini LLM,可准确回答基于文档的问题。
概览
本项目实现了完整的 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
主要功能
- 本地嵌入:使用 Ollama 和 Nomic 模型生成嵌入,无需调用外部 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
在 Google AI Studio 获取 API 密钥。
- 准备文档:
将 .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 距离越小)= 越相似
- 穷举搜索可确保找到最佳匹配项
- 对于多达约 100K 个向量,速度很快
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...
性能优化
适用于大型文档集合(>10K 个块)
- 使用 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:检索更少的块(3 个而非 5 个)
- 使用 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=维度
- 适用范围:最多 1M 个 768 维向量
Nomic 嵌入
- 模型:
nomic-embed-text - 维度:768
- 上下文长度:8192 个 token
- 训练数据:数十亿个用于语义相似度的文本对
- 许可证:开源(Apache 2.0)
分块策略
为什么采用 60 个词并重叠 10 个词?
- 60 个词:约 300-400 个字符,符合典型语义单元(段落)的长度
- 重叠 10 个词:避免在边界处丢失上下文
- 结果:每个分块都包含一些来自相邻分块的上下文
权衡:
- 重叠越多 → 上下文越好,存储需求越大
- 重叠越少 → 处理越快,但可能出现上下文断层
与替代方案的比较
| 组件 | 本项目 | 替代方案 |
|---|---|---|
| 嵌入 | Ollama(本地、免费) | OpenAI($0.0001/1K 个词元) |
| 向量数据库 | 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 界面
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(模型上下文协议)模式进行集成:
- 将 RAG 搜索作为 MCP 工具公开
- 添加用于文档管理的资源
- 参考
../agenticMCP_rag/中的实现
参考资料
许可证
本项目是实验性 AI/ML 单体仓库的一部分。许可证详情请参阅根仓库。
相关项目
如需了解更高级的 RAG 实现,请参阅:
../agenticMCP_rag/- 集成 MCP 和 Agent 架构的 RAG../R_RAG/- 另一种 RAG 实现../perceptionAgent/- 采用感知-决策-行动模式的 Agent 架构
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。