十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MCP 驱动的 Agentic RAG:用 Firecrawl 与 Qdrant 在 Cursor 中构建「本地向量库优先、联网搜索兜底」的知识问答工具

MCP 驱动的 Agentic RAG:用 Firecrawl 与 Qdrant 在 Cursor 中构建「本地向量库优先、联网搜索兜底」的知识问答工具 MCP 驱动的 Agentic RAG用 Firecrawl 与 Qdrant 在 Cursor 中构建「本地向量库优先、联网搜索兜底」的知识问答工具【免费下载链接】ai-engineering-hubIn-depth tutorials on LLMs, RAGs and real-world AI agent applications.项目地址: https://gitcode.com/GitHub_Trending/ai/ai-engineering-hub导读本篇技术指南围绕 ai-engineering-hub 仓库中的 mcp-agentic-rag-firecrawl 示例项目展开讲解如何用MCPModel Context Protocol把「本地向量数据库检索」与「Firecrawl 联网搜索」封装成两个可供 LLM 自主调用的工具并接入 Cursor 作为 MCP 客户端从而在 IDE 内实现一个可复用的Agentic RAG问答系统。读完本文你将掌握从 Qdrant 容器启动、语料向量化入库到 FastMCP 服务端工具注册、Cursor MCP JSON 配置的完整落地链路并理解 LLM 如何依据查询内容在「库内检索」与「联网兜底」之间做路由。一、整体架构与核心组件项目将 Agentic RAG 拆成三个明确的分工角色分别解决「知识从哪来、存在哪、谁来做决策」的问题组件在项目中的角色对应仓库文件Firecrawl爬取/搜索网页数据作为外部知识与联网兜底来源mcp-agentic-rag-firecrawl/server.pyQdrant本地向量数据库承载高频、可控制的领域知识ML FAQ 集合mcp-agentic-rag-firecrawl/rag_code.pyCursor IDEMCP 客户端 / Agent 宿主负责把用户问题路由到正确工具mcp-agentic-rag-firecrawl/README.md整个流程可以概括为本地语料ML FAQ 问答对经 Hugging Face 嵌入模型向量化后写入 Qdrant 集合ml_faq_collection用mcpPython SDK 的 FastMCP 框架把「向量检索」和「Firecrawl 网页搜索」各封装为一个工具tool启动成本地 MCP ServerCursor 通过 JSON 配置把该服务注册为 MCP Server将两个工具暴露给 IDE 内的 LLM当用户提问时由 Cursor 中的 LLM 依据工具描述自主决定ML 相关问题走本地 Qdrant 检索其余问题回退到 Firecrawl 联网搜索。「Agentic」的关键点正在于第 4 步——检索路径不是写死的而是由 Agent 依据工具 docstring 中的路由指引Use this tool when the user asks about ML / not related to general machine learning动态选择从而同时获得私有语料的确定性与联网知识的覆盖面。需要说明本项目的向量检索与 web 搜索是两类完全不同的工具Agentic 决策发生在 MCP 客户端侧Cursor 中的 LLM服务端只负责把工具能力和意图描述暴露给宿主。二、环境准备API Key 与依赖安装2.1 获取 Firecrawl API Key项目中的联网搜索走的是 Firecrawl 的 REST 搜索端点需要先在 Firecrawl 平台注册账号并获取 API Key然后写入项目根目录的.env文件中FIRECRAWL_API_KEY...在 server.py 中该 Key 通过dotenv加载并以Authorization: Bearer key的形式注入 Firecrawl 搜索请求头因此运行前请确认.env就位于工作目录且变量名完全一致。2.2 安装 Python 依赖README 要求 Python 3.11 或更高版本核心依赖安装命令如下pip install firecrawl-py mcp qdrant-client需要说明的是这三者分别是项目链路中各环节的官方 SDK。此外结合仓库源码实际运行还需要以下传递性/配套能力嵌入模型与向量化llama_index的HuggingFaceEmbedding默认使用nomic-ai/nomic-embed-text-v1.5详见 rag_code.pyHTTP 与配置requestsFirecrawl API 调用与python-dotenv读取.env见 server.py进度可视化tqdm用于嵌入与入库批处理时打印进度条见 rag_code.py。首次运行时会从 Hugging Face 拉取嵌入模型权重需要保持网络可用notebook.ipynb 中曾配置cache_folder./hf_cache用于本地缓存模型。三、第一步启动本地 Qdrant 容器项目把 Qdrant 跑在 Docker 中需预先安装 Docker。README 给出的启动命令如下docker run -p 6333:6333 -p 6334:6334 \ -v $(pwd)/qdrant_storage:/qdrant/storage:z \ qdrant/qdrant其中关键参数说明参数含义-p 6333:6333映射 Qdrant REST 端口客户端默认通过http://localhost:6333连接-p 6334:6334映射 Qdrant gRPC 端口配合客户端的prefer_grpcTrue使用可显著提升吞吐-v $(pwd)/qdrant_storage:/qdrant/storage:z把宿主机当前目录的qdrant_storage挂载为容器存储目录实现数据持久化:z适用于 SELinux 环境连接端口的对应关系可以在 rag_code.py 中确认self.client QdrantClient(urlhttp://localhost:6333, prefer_grpcTrue)prefer_grpcTrue意味着客户端优先使用 6334 的 gRPC 通道进行批量操作因此两条端口映射都是必要的。四、构建知识库把 ML FAQ 语料写入 Qdrant容器起来后按 README 指引运行 notebook.ipynb 来创建集合并灌入数据。整段逻辑被沉淀为 rag_code.py由三个类协作完成EmbedData嵌入→ QdrantVDB建库/入库→ Retriever检索。4.1 语料准备项目内置了 20 条机器学习常见问答QA覆盖「建模第一步、数据清洗、归一化 vs 标准化、缺失值处理、类别不平衡、特征选择、过拟合、交叉验证、数据泄漏、可解释性」等主题定义在 rag_code.py。语料以「空行分隔」组织入库前先按\n\n切分再把段落内换行替换为空格得到 20 条[问题答案]的独立文本块new_faq_text [i.replace(\n, ) for i in faq_text.split(\n\n)]4.2 EmbedData本地嵌入模型与批量编码class EmbedData: def __init__(self, embed_model_namenomic-ai/nomic-embed-text-v1.5, batch_size32): self.embed_model_name embed_model_name self.embed_model self._load_embed_model() self.batch_size batch_size self.embeddings [] def _load_embed_model(self): embed_model HuggingFaceEmbedding(model_nameself.embed_model_name, trust_remote_codeTrue) return embed_model def generate_embedding(self, context): return self.embed_model.get_text_embedding_batch(context) def embed(self, contexts): self.contexts contexts for batch_context in tqdm(batch_iterate(contexts, self.batch_size), totallen(contexts)//self.batch_size, descEmbedding data in batches): batch_embeddings self.generate_embedding(batch_context) self.embeddings.extend(batch_embeddings)各参数在实现中的作用参数默认值说明embed_model_namenomic-ai/nomic-embed-text-v1.5开源 embedding 模型输出768 维向量与 Qdrant 建库时vector_dim768严格对应trust_remote_codeTrue允许加载自定义实现代码batch_size32单批送入嵌入模型的文本条数batch 越大吞吐越高但显存/内存占用也越大嵌入完成后embeddata.embeddings中每个元素与embeddata.contexts中的文本一一对应供下一步批量入库。4.3 QdrantVDB建集合与批量入库class QdrantVDB: def __init__(self, collection_name, vector_dim768, batch_size512): ... self.define_client() def define_client(self): self.client QdrantClient(urlhttp://localhost:6333, prefer_grpcTrue) def create_collection(self): if not self.client.collection_exists(collection_nameself.collection_name): self.client.create_collection(collection_nameself.collection_name, vectors_configmodels.VectorParams( sizeself.vector_dim, distancemodels.Distance.DOT, on_diskTrue), optimizers_configmodels.OptimizersConfigDiff( default_segment_number5, indexing_threshold0) ) def ingest_data(self, embeddata): for batch_context, batch_embeddings in tqdm(zip(batch_iterate(embeddata.contexts, self.batch_size), batch_iterate(embeddata.embeddings, self.batch_size)), totallen(embeddata.contexts)//self.batch_size, descIngesting in batches): self.client.upload_collection(collection_nameself.collection_name, vectorsbatch_embeddings, payload[{context: context} for context in batch_context]) self.client.update_collection(collection_nameself.collection_name, optimizer_configmodels.OptimizersConfigDiff(indexing_threshold20000) )建库与入库的参数策略值得展开create_collection的幂等性先用collection_exists判断已存在则跳过避免重复建库报错VectorParams(size768, distanceDOT, on_diskTrue)距离度量选择点积DOT。它要求向量已归一化而nomic-embed-text系列默认产出归一化向量因此点积在数值上等价于余弦相似度on_diskTrue把向量落到磁盘适合在单机内存有限的场景下支撑大数据量OptimizersConfigDiff(default_segment_number5, indexing_threshold0)建库阶段把数据拆成 5 个段并把indexing_threshold设为 0意味着写入后立即建索引保证小数据量场景下检索立即可用ingest_data的两次批量外层按batch_size512对文本与向量成对切批通过payload[{context: ...}]把原文以元数据形式与向量一同写入ml_faq_collection写入后的索引优化入库完成后把indexing_threshold调到20000供数据规模增长后由 Qdrant 按需触发索引构建兼顾写入性能与内存占用。值得注意的一个实现细节差异仓库中的 rag_code.py 在QdrantVDB.__init__内会自动调用define_client()而 notebook.ipynb 中的同名类并不自动连接需要手动执行database.define_client()后再create_collection()。如果你脱离 notebook 直接复用rag_code.py无需重复连接。4.4 检索与验证入库完成后可以用 notebook.ipynb 末尾的调用验证效果result Retriever(database, embeddata).search(How to prevent overfitting?)Retriever.search的实现要点rag_code.pydef search(self, query): query_embedding self.embeddata.embed_model.get_query_embedding(query) result self.vector_db.client.search( collection_nameself.vector_db.collection_name, query_vectorquery_embedding, search_paramsmodels.SearchParams( quantizationmodels.QuantizationSearchParams( ignoreTrue, rescoreTrue, oversampling2.0)), limit3, timeout1000, ) ... final_output \n\n---\n\n.join(combined_prompt) return final_output参数作用get_query_embedding对用户 query 单独编码与文档编码走不同的专用方法query 输入不做长文本截断处理limit3仅取相似度最高的前 3 条作为上下文控制注入 LLM 的 token 量timeout1000单次检索的超时上限毫秒级配置防止大库查询挂死QuantizationSearchParams请求量化搜索路径ignoreTrue让本次请求跳过量化索引、rescoreTrue用原始向量精排、oversampling2.0预取 2 倍候选再做精排兼顾召回率与精度检索命中的结果会取出每条记录 payload 里的context原文用\n\n---\n\n拼接成一段可读上下文文本作为工具返回值交给上层 LLM。五、MCP 服务端把「检索」与「搜索」封装成工具向量库就绪后运行 server.py 启动本地 MCP Server。它基于mcpPython SDK 的FastMCP高层封装from mcp.server.fastmcp import FastMCP from rag_code import * mcp FastMCP(MCP-RAG-app, host127.0.0.1, port8080, timeout30)构造参数值说明MCP-RAG-app服务名客户端配置中通过mcpServers键与此对应host127.0.0.1仅监听本机回环地址MCP Server 与客户端Cursor运行在同一台机器port8080Streamable HTTP 监听端口需与 Cursor JSON 配置一致timeout30服务端请求超时秒注意与客户端配置中timeout: 30000毫秒单位不同5.1 工具一本地向量库检索mcp.tool() def machine_learning_faq_retrieval_tool(query: str) - str: Retrieve the most relevant documents from the machine learning FAQ collection. Use this tool when the user asks about ML. ... if not isinstance(query, str): raise ValueError(query must be a string) retriever Retriever(QdrantVDB(ml_faq_collection), EmbedData()) response retriever.search(query) return response这个工具的 docstring 中明确写着Use this tool when the user asks about ML——这正是 Agent 路由的关键信息。docstring 会被 MCP 宿主Cursor 内的 LLM读取用来判断「什么时候该调它」。函数内部每次调用都会实例化Retriever QdrantVDB(ml_faq_collection) EmbedData对ml_faq_collection集合执行语义检索后返回拼接好的 top-3 上下文文本。5.2 工具二Firecrawl 联网搜索兜底mcp.tool() def firecrawl_web_search_tool(query: str) - list[str]: Search for information on a given topic using Firecrawl. Use this tool when the user asks about a specific topic or question that is not related to general machine learning. ... load_dotenv() url https://api.firecrawl.dev/v1/search payload { query: query, limit: 10, lang: en, country: us, timeout: 60000, ignoreInvalidURLs: False, } headers { Authorization: fBearer {os.getenv(FIRECRAWL_API_KEY)}, Content-Type: application/json } response requests.request(POST, url, jsonpayload, headersheaders) return response.text与本地工具形成互补的是它通过HTTP POST 直连 Firecrawl 的/v1/searchREST 端点并不依赖firecrawl-pySDK。请求参数说明如下请求参数值含义query用户问题原文搜索引擎的查询串limit10返回最多 10 条搜索结果langen限定返回结果语言为英文countryus限定搜索结果地区timeout60000等待 Firecrawl 响应的超时毫秒ignoreInvalidURLsFalse遇到无效 URL 时不静默忽略便于排查docstring 中同样给出了路由指引当问题与通用机器学习无关时使用。于是两个工具构成一条完整的决策链——知识库里有答案就检索知识库没有或不属于该领域就走联网兜底。5.3 启动入口if __name__ __main__: print(Starting MCP server at http://127.0.0.1:8080 on port 8080) mcp.run()直接python server.py即可把服务跑起来mcp.run()会按 FastMCP 的默认传输方式启动 HTTP 服务并持续监听。六、把 MCP Server 接入 CursorMCP 客户端README 给出的最后一步是在 Cursor 中注册该服务。操作路径为Cursor Settings → MCP → Add new global MCP server然后在 JSON 配置文件中写入{ mcpServers: { mcp-rag-app: { command: python, args: [/absolute/path/to/server.py], host: 127.0.0.1, port: 8080, timeout: 30000 } } }配置项与 server.py 的服务端声明一一对应配置字段说明与服务端的一致性要求command: pythonCursor 启动 MCP Server 用的解释器命令若项目用了虚拟环境应换成该 venv 下的python绝对路径必须能 importmcp、rag_code等依赖args脚本路径必须替换为server.py在本机的绝对路径这是启动失败最常见的原因之一指向真实存在的文件host/port连接地址与端口需与 FastMCP 构造时的127.0.0.1:8080一致timeout30000注意这里是毫秒而服务端构造参数timeout30的单位是秒注册成功后Cursor 会自动拉取工具清单。此时在对话中提问即可触发 Agent 路由例如问 How to prevent overfitting? 这类 ML FAQ 问题LLM 会选中machine_learning_faq_retrieval_tool命中本地知识库问与机器学习无关的时效性问题时则会调用firecrawl_web_search_tool走联网搜索。七、整体运行链路与故障排查要点把上述步骤串联起来项目完整运行链路为docker run启动 Qdrant持久化目录为宿主机qdrant_storage按顺序运行 notebook.ipynb切分 FAQ 文本 →EmbedData.embed()生成向量 →QdrantVDB.create_collection()建ml_faq_collection→ingest_data()批量入库 →Retriever.search()自测召回确认.env中存在有效的FIRECRAWL_API_KEYpython server.py启动 MCP Server监听127.0.0.1:8080在 Cursor 的 MCP 配置中按上文 JSON 注册并连接在 Cursor 对话中测试两类提问观察工具选择与返回内容。常见问题与排查方向结合源码逻辑推导供实操参考向量库检索返回空确认ml_faq_collection已成功创建并入库——create_collection对已存在集合会直接跳过若首次建库失败需先清掉旧集合再重试同时确认 Qdrant 容器仍在本机 6333/6334 端口监听。联网搜索报 401/403检查 server.py 读取的FIRECRAWL_API_KEY是否通过load_dotenv()正确加载且.env位于启动 MCP Server 的工作目录。Cursor 连接失败核对客户端 JSON 中host/port/timeout与 FastMCP 构造参数一致尤其注意timeout的秒/毫秒单位差异以及args必须是server.py的绝对路径。工具选型不符合预期工具 docstring 是 LLM 路由的依据可参照 server.py 与 server.py 的写法把「何时该用本工具」写得更明确具体。小结通过这个示例项目可以完整看到 Agentic RAG 的一种轻量落地范式用 FastMCP 把私有向量库检索和Firecrawl 联网搜索声明为带意图描述的工具交给支持 MCP 的 IDE 宿主Cursor做运行时路由。向量维度的对齐nomic-embed-text 768 维 ↔ Qdrant 集合维度、批量嵌入与入库的切批策略、DOT 距离与归一化向量的配套选择、工具 docstring 对 Agent 路由的引导是让这条链路真正可用的四个关键细节。对本地知识仓库的rag_code.py与云端知识server.py的 Firecrawl 调用的封装也体现了把私有语料可控性与实时联网扩展性相结合的常见生产化思路。【免费下载链接】ai-engineering-hubIn-depth tutorials on LLMs, RAGs and real-world AI agent applications.项目地址: https://gitcode.com/GitHub_Trending/ai/ai-engineering-hub创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表