在企业数字化转型浪潮中,高效管理与利用内部知识资产已成为关键挑战。随着大型语言模型(LLM)技术的成熟,检索增强生成(RAG)应用正逐步成为连接企业知识与AI能力的重要桥梁。然而,传统RAG实现常面临检索质量不佳、实时更新困难等痛点。本文将通过一个完整的实战案例,详细介绍如何基于模型上下文协议(MCP)构建一套高性能的企业RAG系统,帮助企业快速打造智能知识库应用。
MCP与传统RAG对比优势
本节将深入剖析传统RAG方案的局限性,并展示MCP协议如何通过标准化与解耦设计解决这些痛点。
传统RAG方案的四大局限与挑战
传统RAG实现通常采用简单的“Embedding+检索+LLM生成”架构,这种方案在实际应用中存在以下四个主要限制:
- 1. 紧耦合架构:检索逻辑与LLM调用紧密耦合在一起,导致系统难以独立优化。例如,若想更换检索算法,可能需要修改大量与LLM生成相关的代码。
- 2. 单一检索策略:通常只采用向量检索这一种方式,缺乏多种检索方式(如关键词检索、混合检索)的结合,使得在特定场景下的召回率不高。
- 3. 缺乏标准化接口:不同RAG实现之间的接口差异巨大,导致功能复用困难。当企业需要集成多个数据源时,需要为每个数据源编写适配代码。
- 4. 维护成本高:系统升级或功能迭代时,需要修改大量底层代码,导致维护成本高昂,不利于长期发展。
小提示: 传统RAG的“紧耦合”特性是许多企业痛点的主要来源。如果您的团队在维护现有RAG系统时感觉越来越吃力,这通常意味着系统架构需要向解耦方向优化。
MCP解决方案的四大核心优势
基于MCP的RAG系统通过标准化协议,将知识检索服务解耦为独立模块,从而解决了上述问题。它带来了以下显著优势:
- 1. 标准化工具调用:MCP提供统一的接口规范,开发人员无需为不同数据源或服务编写定制化连接器,大大降低了集成成本。
- 2. 解耦设计:模型调用与业务逻辑完全分离,这意味着您可以独立升级LLM模型或修改检索策略,而不影响系统其他部分。
- 3. 灵活扩展:可以轻松添加新的数据源和功能模块,例如混合检索、多模态内容(图片、视频等),使系统具备良好的可扩展性。
- 4. 工程实践友好:由于接口的标准化和模块化设计,这种架构符合软件工程的最佳实践,非常便于团队进行协作开发。
(图片源自 dailydoseofds)
项目背景与需求
现代企业知识管理的挑战
现代企业在内部知识管理方面普遍面临以下四类挑战:
- 知识分散:企业文档通常分布在多个系统(如本地磁盘、云端网盘、OA系统)中,缺乏统一的检索入口,员工查找信息效率极低。
- 检索效率低:传统的基于关键词的检索方式无法理解用户的语义意图,经常检索出大量不相关的结果,难以准确找到所需信息。
- 知识更新慢:知识库的更新严重依赖人工整理和录入,导致库内信息往往滞后于实际情况,无法及时反映最新的变化。
- 使用门槛高:专业术语和复杂的查询语法提高了普通员工的使用难度,使得知识库往往只能被少数技术人员或特定岗位有效利用。
系统的核心需求
针对上述问题,我们需要设计一个系统,它必须满足以下四个核心需求:
- 智能检索:支持用户使用自然语言进行提问,系统能够理解问题的意图和上下文,而不是依赖死板的关键词匹配。
- 知识自动化处理:能够实现文档的智能拆分(保证语义完整),并自动从文档中提取出核心的问答对(FAQ),减少人工操作。
- 灵活扩展:系统必须支持多种不同类型的数据源(如Markdown文件、PDF、网页)和不同的LLM模型(如GPT-4、Claude等)的集成。
- 易于部署与维护:架构设计应简洁明了,方便技术团队快速掌握并进行后续的迭代与维护。
项目目标
本项目旨在构建一个基于MCP的企业RAG系统,并实现以下具体目标:
1. 技术目标
- 构建一个支持MCP协议的知识库服务端(Server)和客户端(Client)。
- 实现文档的智能切分功能,以及从文本中自动提取FAQ的功能。
- 支持复杂问题的拆解分析和混合检索策略(向量检索 + 全文检索)。
2. 应用目标
- 提供一个统一的知识库管理和检索入口,方便用户访问。
- 显著提升企业内部知识检索的准确率,目标是将检索结果的相关性提升至90%以上。
- 通过自动化处理流程,减少70%的知识库维护工作量。
- 支持企业内各种常见文档类型(如技术文档、操作手册、FAQ)的智能处理和检索。
项目系统设计与实现
本项目系统设计参考自
alibabacloud-tablestore-mcp-server[1]。由于原项目使用Tablestore存储和Java实现,不利于后期扩展和迭代,我们将其改造为Milvus向量数据库和Python语言实现的MCP Server和MCP Client,代码完全重写。以下设计和流程皆为改造后的内容,在此感谢 @xjtushilei 开源的alibabacloud-tablestore-mcp-server。
我们构建的基于MCP的RAG系统主要包含三个核心部分:
- 知识库服务(MCP Server):这是一个基于Milvus向量数据库的后端服务,负责文档的存储和检索。
- 客户端工具(MCP Client):这是一个与MCP Server通信的客户端,它负责执行知识库的构建和检索功能。
- 大模型集成:系统通过LLM(大语言模型)实现一系列核心功能,如文档切分、FAQ提取、问题拆解和最终的回答生成。
系统整体工作流程
系统的工作流程主要分为两大阶段:知识库构建和知识检索(RAG)。
1. 知识库构建
- 文本切段:对原始文本进行智能分段。这一步非常关键,需要保证切分后的每一个段落都能保持内容的完整性和语义的连贯性,而不是粗暴地按字符数截断。
- 提取FAQ:利用LLM,根据文本内容自动提取常见的问答对(FAQ)。这些FAQ作为知识库检索的重要补充,能显著提升对特定问题的检索效果。
- 导入知识库:将切分好的文本段和提取出的FAQ,经过Embedding(向量化)处理后,统一导入到Milvus向量数据库中。
2. 知识检索(RAG)
- 问题拆解:当收到一个复杂问题时,系统会先利用LLM对其进行拆解和重写,拆解成多个更简单、更原子的子问题。
- 混合检索:针对每个子问题,系统会分别检索“知识库(Knowledge Store)”和“FAQ库(FAQ Store)”。对于知识库,主要采用向量检索;对于FAQ库,则采用向量检索与全文检索相结合的混合检索模式。
- 内容筛选:针对检索出来的所有内容进行相关性筛选,只保留与当前问题最相关的高质量片段,作为最终回答的参考。
相比传统的Naive RAG,本系统在知识库构建和检索环节分别做了常见的优化,包括Chunk切分优化、提取FAQ、Query Rewrite(问题重写)、混合检索等。
系统架构与项目结构
本系统(Agent)的整体架构分为三个部分:
- 知识库:内部包含Knowledge Store和FAQ Store,分别存储文本内容和FAQ内容,并支持向量和全文的混合检索。
- MCP Server:提供对Knowledge Store和FAQ Store的读写操作,总共提供4个标准化的Tools(工具)。
- 功能实现部分:完全通过精心设计的“Prompt提示词 + LLM”来实现对知识库的导入、检索和问答等核心功能。
项目结构主要分为两个目录:
milvus-mcp-client:这是用Python实现的客户端。它负责与大模型进行交互,通过MCP Client获取并调用MCP Server提供的Tools。其核心功能(知识库构建、检索、问答)由一系列Prompt驱动。milvus-mcp-server:这是用Python实现的服务端。它基于MCP框架实现,提供了连接Milvus向量数据库的接口,负责知识库的底层存储和检索功能。
项目实战:从零搭建MCP-RAG系统
接下来,我们将从环境搭建、服务部署到功能测试,全面介绍如何搭建一个基于MCP的RAG系统。
环境准备
首先,请确保您的开发环境满足以下系统要求:
- Docker和Docker Compose:用于启动Milvus及其依赖组件。
- 硬件要求:至少4个CPU核心、4GB内存和20GB磁盘空间。
- 克隆代码:
git clone -b rag_0.1.1 https://github.com/FlyAIBox/mcp-in-action.git
小提示: 如果您使用的是Windows系统,建议通过WSL2(Windows Subsystem for Linux)来运行Docker,以获得更好的兼容性和性能。
部署MCP Server
MCP Server基于Milvus向量数据库,提供了知识库的存储和检索核心功能。对于需要进行开发或调试的场景,推荐采用本地部署方式:
# 进入项目目录
cd mcp-rag
# 先启动Milvus及依赖服务
docker compose up -d etcd minio standalone
# 创建Python虚拟环境
python -m venv env-mcp-rag
source env-mcp-rag/bin/activate
# 安装依赖
pip install -r requirements.txt
# 启动服务
python -m app.main
MCP Server的核心API
MCP Server提供四个核心工具,支持对知识库的读写操作:
- storeKnowledge:将文档内容存储到知识库中。
- searchKnowledge:在知识库中搜索与输入问题最相似的文档。
- storeFAQ:将FAQ对存储到FAQ库中。
- searchFAQ:在FAQ库中搜索相似问答对。
让我们看看这些API的核心实现原理,以 storeKnowledge 为例:
async def store_knowledge(self, content: str, metadata: Dict[str, Any] = None) -> Dict[str, Any]:
"""存储知识内容到Milvus"""
# 确保服务准备就绪
await self.ready_for_connections()
try:
knowledge_content = KnowledgeContent(
content=content,
metadata=metadata or {}
)
self.milvus_service.store_knowledge(knowledge_content)
return {"status": "success", "message": "Knowledge stored successfully"}
except Exception as e:
logger.error(f"Error storing knowledge: {e}")
return {"status": "error", "message": str(e)}
代码解读: 这段代码展示了 storeKnowledge 工具的典型工作流程:接收文本内容(content)和一个可选的元数据字典(metadata),然后创建一个知识内容对象,并通过 milvus_service 将其存储到Milvus向量数据库中。
基于MCP Client实现RAG客户端
接下来,我们需要实现一个RAG客户端,通过MCP协议与Server通信,实现知识库的构建和查询功能。
1. 知识库构建
- 文本切分:对长文本进行智能切分,保证语义完整性。
- FAQ提取:从文档中自动生成常见问题解答对。
- 向量化存储:将文本片段和FAQ转换为向量并存入Milvus。
文本切分代码示例:
def _chunk_text(self, text: str) -> List[str]:
"""将文本分割成chunk,保证语义完整性"""
chunks = []
# 处理文本小于chunk_size的简单情况
if len(text) <= self.chunk_size:
chunks.append(text)
return chunks
# 使用重叠策略分割文本
start = 0
while start < len(text):
# 获取chunk结束位置
end = start + self.chunk_size
# 调整结束位置,避免在句子中间切断
if end < len(text):
# 寻找句子边界(句号、问号、感叹号)
sentence_end = max(
text.rfind('. ', start, end),
text.rfind('? ', start, end),
text.rfind('! ', start, end)
)
# 如果找到句子结束,使用它作为chunk结束
if sentence_end > start:
end = sentence_end + 1 # 包含句号
# 添加chunk
chunks.append(text[start:min(end, len(text))])
# 移动开始位置到下一个chunk,考虑重叠
start = end - self.chunk_overlap
# 确保进度
if start >= len(text) or start <= 0:
break
return chunks
小提示: 文本切分时使用“重叠(overlap)”策略是非常实用的技巧。它可以有效避免检索时,问题恰好落在两个chunk的切割线上,导致上下文丢失的问题。
FAQ提取,通过LLM实现:
async def _extract_faqs(self, text: str) -> List[Dict[str, str]]:
"""从文本中提取FAQ"""
# 对过长文本进行分块处理
if len(text) > 8000:
chunks = self._chunk_text(text)
faqs = []
for chunk in chunks:
chunk_faqs = await self._extract_faqs(chunk)
faqs.extend(chunk_faqs)
return faqs
# FAQ提取的提示模板
system_prompt =
"""你是一位专业的知识提取专家。你的任务是从文本中提取可能的常见问题(FAQ)。
这些问题应该是用户可能会问的关于文本内容的自然问题,答案应该能在文本中找到。
提取的FAQ应该覆盖文本中最重要的概念和信息。
请遵循以下规则:
1. 每个FAQ由一个问题和一个答案组成
2. 问题应该简短明了,直接针对主题
3. 答案应该全面但简洁,提供文本中的相关信息
4. 提取的FAQ数量应该基于文本长度和内容丰富度,通常不超过10个
5. 确保提取的FAQ相互之间不重复
6. 按照重要性排序,最重要的问题应该放在前面
输出格式必须是一个JSON数组,每个FAQ是一个包含"question"和"answer"字段的对象,例如:
[
{
"question": "问题1?",
"answer": "答案1"
},
{
"question": "问题2?",
"answer": "答案2"
}
]
只输出JSON格式,不要有任何其他文本。"""
user_prompt = f"""从以下文本中提取常见问题(FAQ):
```
{text}
```
请提取最相关、最有价值的FAQ,并按JSON格式返回。"""
# 使用LLM提取FAQ
response = self.llm_client.sync_generate(
prompt=text,
system_prompt=system_prompt,
temperature=0.3
)
# 解析LLM响应获取FAQ
# ...
2. 知识检索优化
与传统RAG不同,我们在检索环节引入了三个优化机制:问题拆解、混合检索和结果筛选。
- 问题拆解:将复杂问题拆解为多个子问题。
- 混合检索:同时检索文本库和FAQ库,提高召回率。
- 结果筛选:对检索结果进行排序和筛选,优先保留高质量内容。
问题拆解示例:
async def _decompose_question(self, question: str) -> List[str]:
"""将复杂问题分解为更简单的子问题"""
system_prompt =
"""你是一位问题分析专家。你的任务是将复杂问题分解为更简单的子问题,以便更好地检索相关信息。
请遵循以下规则:
1. 分析用户的问题,识别其中包含的不同方面或概念
2. 将复杂问题拆分成更简单、更具体的子问题
3. 确保子问题覆盖原始问题的所有关键方面
4. 提供2-4个子问题,具体数量取决于原始问题的复杂度
5. 子问题应该是明确的、有针对性的
6. 子问题之间应该尽量避免重复
输出格式必须是一个JSON数组,包含所有子问题的字符串,例如:
["子问题1", "子问题2", "子问题3"]
如果原始问题已经足够简单,不需要分解,则返回只包含原始问题的JSON数组:
["原始问题"]
只输出JSON格式,不要有任何其他文本。"""
user_prompt = f"""请将以下问题分解为更简单的子问题以便检索:{question}"""
# 使用LLM生成子问题
response = self.llm_client.sync_generate(
prompt=user_prompt,
system_prompt=system_prompt,
temperature=0.3
)
# 解析响应获取子问题列表
# ...
结果筛选与生成回答的关键代码:
async def filter_context(self, question: str, context_items: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""根据问题相关性筛选上下文"""
# 简单筛选:去重和截断
seen_contents = set()
filtered_items = []
# 优先处理FAQ类型
faq_items = [item for item in context_items if item["type"] == "faq"]
knowledge_items = [item for item in context_items if item["type"] == "knowledge"]
# 先处理FAQ项
for item in faq_items:
# 去重处理
# ...
# 再处理知识项
for item in knowledge_items:
# 去重处理
# ...
# 限制上下文项总数
max_context_items = 6
if len(filtered_items) > max_context_items:
filtered_items = filtered_items[:max_context_items]
return filtered_items
实际效果展示
部署完成后,让我们看看系统的实际运行效果。
1. 知识库构建
python -m app.main build --file test.md --title "RAG基本介绍" --author "企业知识库" --tags "LLM,RAG,知识库"
执行结果:
2025-05-11 14:50:16 | INFO | app.knowledge_builder:build_from_text:52 - Split text into 2 chunks
2025-05-11 14:50:59 | INFO | app.knowledge_builder:build_from_text:72 - Extracted 8 FAQs from text
2025-05-11 14:51:00 | INFO | __main__:build_knowledge_base:48 - Stored 2/2 chunks to knowledge base
2025-05-11 14:51:00 | INFO | __main__:build_knowledge_base:50 - Extracted and stored 8 FAQs
从上图可以看出,系统成功将一段长文本切分为2个逻辑段落,并自动从中提取了8个高质量的FAQ问答对,整个过程完全自动化。
2. 知识检索问答
python -m app.main query --question "RAG相比企业传统的知识库有什么优势和缺点"
执行结果:
2025-05-11 15:01:46 | INFO | app.knowledge_retriever:query:39 - Decomposed question into 4 sub-questions
2025-05-11 15:01:47 | INFO | app.knowledge_retriever:query:67 - Filtered 28 context items to 6
================================================================================
问题: RAG相比企业传统的知识库有什么优势和缺点
--------------------------------------------------------------------------------
回答: 检索增强生成(RAG)是一种通过整合训练数据之外的权威知识库来优化大型语言模型(LLM)输出的技术。其核心在于允许LLM在生成响应前动态访问特定领域或组织的内部知识库,例如实时数据源、文档或专业数据库,而无需对模型本身进行重新训练。这种方式通过引入外部信息,显著提升了生成内容的相关性、准确性及实用性,同时保留了LLM的灵活性和泛化能力。
================================================================================
从执行结果可以看到,系统首先将用户问题拆解为4个子问题,然后从知识库中检索到28个相关的上下文片段,经过筛选后保留了最相关的6个。最终,系统基于这些筛选后的信息,生成了准确且全面的回答。
实施建议与最佳实践
根据实际项目经验,我们总结了以下六个关键的最佳实践:
1. 文档处理策略
- 合理设置切分参数:文本切分的大小推荐设置在1000-1500字符之间,重叠率推荐在200-300字符。这个范围在实践中被证明是效果较好的平衡点。
- 区分文档类型:例如,技术文档可以按章节或段落切分,而叙述性文档则需要更关注语义完整性。不应粗暴使用统一标准。
- 保留元数据:在存储时,应尽量保留文档的原始格式信息作为元数据(如作者、创建时间、标签等),这能显著提升后续检索的精确度。
2. 检索优化技巧
- 使用混合检索:务必采用“语义+关键词”的混合检索模式,这能有效提升召回率。仅依赖向量检索可能对专业术语或生僻词检索失败。
- 控制子问题数量:在问题拆解环节,建议将子问题的数量控制在2-4个,数量太少可能导致信息覆盖不全,太多则可能引入噪声。
- 限制上下文数量:最终提供给LLM生成的上下文片段数量应限制在5-8个之间,以避免信息过载,影响模型输出的聚焦度和质量。
3. 常见问题 (Q&A)
Q: MCP协议具体是什么?它和普通的API有什么区别?
A: 模型上下文协议(MCP)是一种开放标准,旨在为AI应用与外部数据源和工具之间提供一个统一的接口。与普通API相比,MCP的主要优势在于其标准化和解耦特性。普通的API接口格式各异,需要为每个服务编写特定的适配器;而MCP仿照了IDE中的LSP协议思想,为AI应用提供了一种“即插即用”的插拔式体验,开发人员只需实现MCP标准接口,任何支持MCP的AI客户端都能直接调用,无需额外集成。
Q: 为什么选择Milvus作为向量数据库,而不是其他方案?
A: Milvus是一个高性能、云原生的向量数据库。它专为处理海量向量数据设计,在本项目中主要基于以下几点考虑:第一,它提供了丰富的检索能力,包括向量检索、标量过滤和混合检索;第二,它支持Docker化部署,易于在本地或生产环境搭建;第三,其SDK对Python非常友好,便于我们进行快速的开发和集成。相比Elasticsearch等方案,Milvus在向量检索性能上更具优势。
Q: 如果我的企业数据量非常大(百万级文档),这个方案还能用吗?
A: 完全可以。本方案基于Milvus向量数据库构建,Milvus本身就是为了处理大规模、高维度的向量数据而设计的,支持分布式集群部署。当数据量增长时,可以通过增加Milvus集群的节点数(水平扩容)来应对。同时,在客户端层面,我们也可以通过优化文本切分策略、使用更高效的Embedding模型、以及调整检索的Top-K参数等手段,来保证系统在数据量变大时的响应速度。
小提示: 对于数据量非常大的场景,建议在生产环境中使用专门的Embedding服务(如BAAI/bge系列模型或OpenAI的Embedding API),并考虑使用异步处理框架来构建索引,以提高吞吐量。
总结与展望
基于MCP实现的RAG系统代表了知识库建设的新方向。通过模型上下文协议,我们不仅成功解决了传统RAG系统中“紧耦合”、“检索策略单一”和“维护成本高”等痛点,还为企业提供了一种低成本、高效率的知识管理方案。这种架构允许企业灵活地替换底层的LLM模型或上游的数据源,而不需要对整个系统进行重构。
未来,随着大模型技术的进步和MCP标准的不断完善,我们可以期待更多创新功能的出现:
- 多模态内容的支持:系统将不仅能检索和生成文本,还能处理图像、音频、视频等多种类型的内容。
- 更精准的实时知识更新机制:通过更先进的事件驱动或增量索引策略,实现知识的即时、无感知更新。
- 基于用户反馈的自适应检索优化:系统可以根据用户对过往搜索结果的反馈(如点赞、踩),自动调整检索策略和权重,实现自我进化。
对于企业而言,现在正是开始探索和应用这一技术的最佳时机。通过MCP-RAG,企业可以充分挖掘自身知识资产的价值,为员工和客户提供更智能、更精准、更高效的信息服务。
