RAG与MCP:您的AI应用究竟需要哪一个?
RAG与MCP:RAG用于读取文档,而MCP则用于操作实时系统。两者均基于代码构建,同一个金融助手可通过三种方式实现,且有一个简单的选择规则。
人们常常会问自己是否需要RAG或MCP,仿佛这两种技术是菜单上的竞争对手。其实并非如此,一旦理解了它们的原理,选择就会变得近乎理所当然。以下是简短说明。
- RAG通过获取相关文本并将其放入提示词中,为模型提供训练时未曾见过的知识,模型随后会读取这些内容。
- MCP是一种标准协议,模型可通过它调用工具并对系统执行操作,即让模型真正完成某项任务。
- 简单的区分方式是:RAG关乎文档中包含的内容,而MCP关乎系统能够实现的功能。
- 两者并不竞争。RAG是一种技术,MCP是一种协议,因此“选RAG还是MCP?”就好比问“选缓存还是HTTP?”一样。
以一个看似简单但实际上并非如此的问题为例:苹果公司今天的市盈率是多少?如果用RAG来解决,得到的会是一个看似可靠但实际错误的答案;而使用MCP则能得到正确结果。
相同的域名、相同的用户,却有着截然不同的架构。接下来的内容将从基本原理出发探讨这两个概念,用三种方式构建一个财务助手,最后给出一个无需过多思考即可应用的决策规则。
RAG是什么
其名称全称为检索增强生成。用通俗的话来说,它包含三个步骤:检索与用户问题最相关的文本片段,将这些片段插入提示词中以增强内容,然后基于这些补充的上下文生成答案。
可以想象一场不允许携带笔记的考试,但有一名研究助理在考场外等候。考生将问题从门下塞进去,助理便跑去图书馆,挑选出最相关的三页内容再从门下传回,考生便根据这些内容写出答案。实际上并没有任何学习发生,只是合适的页面在恰当的时刻出现了而已。
这就是RAG。模型的权重永远不会改变,因为被编辑的是提示词而非模型本身。
为何需要向量数据库
最困难的部分是第一步,即找到正确的页面。关键词搜索往往无法奏效,因为人们提出的问题很少会使用与原文相同的词汇。有人会询问“亏损的公司”,而文档中写的是“负净收入”:虽然含义相同,但没有共同的词汇。
嵌入技术可以解决这个问题。这类模型会将文本转换为向量,即一组数字,使得含义相似的内容彼此靠近。“亏损的公司”和“负净收入”尽管没有共同的词汇,但最终会成为相邻的向量。向量数据库则存储这些向量,并能快速回答一个问题:哪个向量与当前向量最接近?
代码中的RAG
下面是一个不依赖任何框架的简易实现,因此所有运作环节都清晰可见。它存储了一些金融相关片段,将其嵌入系统中,根据与问题的余弦相似度对它们进行排序,然后将最匹配的片段传递给聊天模型。
from openai import OpenAI
client = OpenAI()
# Your knowledge base, already chunked.
DOCUMENTS = [
"The P/E ratio divides share price by earnings per share. "
"When earnings are negative, P/E is undefined and usually shown as N/A.",
"EV/EBITDA is often preferred over P/E for capital-intensive companies "
"because it is unaffected by capital structure and depreciation policy.",
"The PEG ratio adjusts P/E by the expected earnings growth rate. "
"A PEG below 1.0 is traditionally read as undervalued.",
]
def embed(text: str) -> list[float]:
"""Turn text into a vector."""
response = client.embeddings.create(
model="text-embedding-3-small",
input=text,
)
return response.data[0].embedding
def cosine_similarity(a: list[float], b: list[float]) -> float:
"""How close are two vectors? 1.0 means identical direction."""
dot = sum(x * y for x, y in zip(a, b))
norm_a = sum(x * x for x in a) ** 0.5
norm_b = sum(y * y for y in b) ** 0.5
return dot / (norm_a * norm_b)
# Index once, reuse many times. In production this lives in a vector DB.
INDEX = [(doc, embed(doc)) for doc in DOCUMENTS]
def retrieve(question: str, k: int = 2) -> list[str]:
"""Step 1: find the most relevant chunks."""
q_vector = embed(question)
scored = [
(cosine_similarity(q_vector, vector), doc)
for doc, vector in INDEX
]
scored.sort(reverse=True)
return [doc for _, doc in scored[:k]]
def answer(question: str) -> str:
"""Steps 2 and 3: augment the prompt, then generate."""
context = "\n\n".join(retrieve(question))
prompt = (
f"Answer using only the context below.\n\n"
f"Context:\n{context}\n\n"
f"Question: {question}"
)
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
)
return response.choices[0].message.content
print(answer("What do I use when a company has negative earnings?"))
看看发生了什么。该模型从未接受过那三段文本的训练,却依然给出了正确答案,因为其提示词中包含了正确的信息片段。
再看看这个限制。RAG只能返回已索引的内容。如果苹果公司的市盈率今天早间发生了变化,而索引是上个月生成的,RAG仍会引用上个月的数值,并表现得极为确定。正是由于这一限制,才有了MCP的存在。
什么是MCP
MCP,即模型上下文协议,由Anthropic于2024年11月推出。据报道,2025年12月它被转交给了Linux基金会旗下的Agentic AI Foundation,因此现在Claude、ChatGPT、Gemini、Cursor以及VS Code都支持该协议,而不再属于某一家公司的私有格式。
它是一种用于连接AI模型与工具的标准。它并非某种技术,而是一种协议,与HTTP或SQL属于同一类别。
在USB-C出现之前,每款设备都有自己独特的接口,因此15款设备就需要15种不兼容的电缆。在MCP出现之前,AI工具也是这种情况。要将10个AI应用与100种工具连接起来,可能需要编写多达1,000个定制化的集成方案,每个方案都是独立设计的,也需要单独维护。MCP则将这一切简化为统一的标准。工具提供商只需实现一次该协议,所有兼容的客户端就能使用它;而AI应用只需实现一次客户端部分,就能接入所有工具。
MCP服务器提供什么
- 工具是可调用的函数。调用如
get_stock_price("AAPL.US")这样的代码时,会实际执行相关代码并返回真实结果。 - 资源是模型可以读取的数据,比如文档或文件内容。
代码中的MCP
一个可运行的服务器结构很简单。下面的示例仅包含了引语查询和两只股票对比功能,仅此而已。
from mcp.server.fastmcp import FastMCP
import httpx
mcp = FastMCP("finance-tools")
@mcp.tool()
def get_current_price(ticker: str) -> dict:
"""Get the latest price for a stock ticker.
The docstring matters more than you'd think. It is what the
model reads to decide whether to call this tool at all.
"""
response = httpx.get(f"https://api.example.com/quote/{ticker}")
return response.json()
@mcp.tool()
def compare_tickers(ticker_a: str, ticker_b: str) -> dict:
"""Compare two tickers on price, market cap and P/E ratio."""
return {
"a": get_current_price(ticker_a),
"b": get_current_price(ticker_b),
}
if __name__ == "__main__":
mcp.run()
只需一条命令即可将其注册到 Claude Code 中:
claude mcp add finance -- python /path/to/server.py
该模型现在能够实际执行操作了。它不是读取价格信息,而是直接获取价格。
一个容易被忽视的细节:文档字符串其实就是接口。模型是否调用某个工具,完全取决于对该工具的描述方式。如果描述模糊,就会导致出现没人使用的工具,而这正是正确安装的MCP服务器看似毫无反应的常见原因。
实际差异
将两者放在一起对比,它们在本质和功能上都有所不同。RAG是一种从索引中读取数据的技术,而MCP则是用于连接实时系统的协议。RAG能让模型掌握更多信息,MCP则能让模型具备执行任务的能力。由于属于向量查找方式,RAG每次查询的成本都很低且速度快,而MCP的成本则取决于其底层API的费用。当索引过时后RAG也会失效,而MCP则会返回来源当前提供的内容。
这条规则用一句话就能概括。需要在文档中回答?用RAG。需要在系统中回答?用MCP。
用实际问题来测试吧。“PEG比率是什么意思?”这类问题存在于文档中,因此适用RAG;“微软的股价是多少?”这类问题存在于系统中,因此适用MCP;“下订单”或“发送这份报告”属于操作类任务,无论哪种情况都适用MCP,因为检索功能本身无法执行任何操作;“我们的退款政策对已开启的商品有何规定?”这类问题只是静态文本,因此适用RAG。
为何人们总是纠结两者是否相同
这种混淆只有一个原因:MCP服务器可以将文档作为资源进行发布,这看起来与检索功能类似。但两者的区别在于规模和机制——资源仅限于模型能通过ID找到的少量文档,而RAG则能处理数千份文档,需要通过语义排序来筛选出相关的三条信息。
这两种方式也可以结合使用。在MCP服务器中,你可以运行整个RAG流程,并将其作为search_knowledge_base工具呈现给模型。模型随后会调用该工具,即MCP部分,而该工具负责执行检索操作,也就是RAG部分。
同一个助手,三种实现方式
抽象比较只能起到有限的作用,因此可以分别用三种方式构建金融分析助手,看看它们在哪些方面存在缺陷。金融领域是很好的测试案例,因为同一领域内既有永远不会变化的定义,也有每秒都在变化的数字。对于市场数据,像EODHD这样的数据提供商很适合用于演示,因为它同时提供了传统的REST API和官方的MCP服务器,这样就可以在不更换数据提供商的情况下比较不同架构。
版本A:仅RAG
将金融知识库进行索引,然后从中获取答案。该知识库由简短的定义列表构成,实际系统中还会包含数千条更多内容。
# Building a knowledge base of financial concepts
CORPUS = [
"Market capitalization equals share price multiplied by shares outstanding.",
"The P/E ratio compares share price to earnings per share.",
"Free cash flow is operating cash flow minus capital expenditures.",
"A dividend yield above 6% often signals either a falling share price "
"or an unsustainable payout ratio.",
# ...plus a few thousand more chunks
]
索引完成后,会提出一个概念性问题:
# Index them, then:
answer("Explain what a high dividend yield might indicate")
那个案例很合适。其核心理念不会随时间改变,答案存在于语料库中,检索系统只需将其找出来即可。接下来是一个实时问题:
answer("What is Apple's current dividend yield?")
这种做法彻底失败了,而且是以最糟糕的方式。模型检索到一段解释股息收益率含义的文本,记得在训练过程中接触过一些苹果公司的相关数据,于是输出了类似“苹果公司的股息收益率约为0.5%”的内容。虽然表述流畅、格式良好,但信息可能已经过时数月,而整个处理流程却没有任何机制能识别出问题。这就是人们往往低估的RAG故障模式:它不会引发错误,只会返回流畅但过时的文本。
版本B:仅使用MCP
在这种模式下,模型直接连接实时数据源。
# EODHD publishes two endpoints. v2 uses OAuth, v1 uses an API key.
claude mcp add --transport http eodhd https://mcp.eodhd.com/v2/mcp
再次提问时,同一个问题会通过不同的路径处理。模型会调用resolve_ticker("Apple")来获取AAPL.US,然后再调用基本面分析工具来取得今日数据。该数值是可信的,因为它是通过实时查询获得的而非预先存储的。
无论使用哪种服务提供商,都值得研究该服务器中的两项设计选择,因为它们解决了所有工具调用系统都会遇到的问题。首先,股票代码解析是一个独立的一级工具。用户会说“Apple”,但API需要的是AAPL.US;用户会说“Deutsche Bank”,但API需要的是DBK.XETRA。一个能够处理各种名称、不完整的股票代码及ISIN代码,并在公司同时在多个交易所交易时提供替代选项而非盲目猜测的解析器,能够消除故障点。金融交易系统中许多工具调用失败恰恰发生在这一环节。
其次,文档以资源的形式嵌入其中。据称该服务器会将上百页的自身API文档作为MCP资源提供,这样模型无需发起API调用即可知晓某个接口接受哪些参数。从架构角度来看这很有意思:一种类似RAG的模式存在于MCP服务器内部,静态参考资料作为资源,实时数据作为工具,各自发挥最佳作用。
版本C:两者兼备,即最终发布的版本
单独使用任一版本都无法打造出令人满意的产品。基于RAG的版本无法查询当前价格,而基于MCP的版本则要浪费一次API调用来解释市盈率。真正的设计会将每个问题发送到相应的子系统处理。
def route(question: str) -> str:
"""Decide which subsystem answers this question."""
# Signals that the question is about live state
live_signals = ["current", "today", "now", "latest", "price", "quote"]
if any(signal in question.lower() for signal in live_signals):
return "mcp" # fetch it
return "rag" # look it up
上述的关键词测试刻意设计得较为简单。实际生产环境中使用的路由器通常是这样的:提供检索工具和实时数据工具,详细说明每种工具的功能,让系统自行选择。下方的分类保持不变。
实际应用中的流程如下:
- “市盈率衡量的是什么?”这类问题交由RAG处理:即时响应且几乎无需成本。
- “微软目前的市盈率是多少?”这类问题交由MCP处理:通过一次API调用即可获取实时数据。
- “微软的市盈率在其所属行业里算高吗?”这类问题需要同时使用两种工具:MCP提供数值,RAG提供用于解读该数值的行业背景信息。
正是第三种类型的问题体现出组合使用的优势,而且也最接近真实用户的问题场景。
容易与此类问题混淆的对比
在RAG与MCP的混淆中,还有三对概念经常被一起提及,每对都可以很快澄清。
MCP与API的区别。 MCP服务器通常是API的封装层,而非替代品。两者的差异在于目标使用群体:REST API适合那些会阅读文档并编写集成代码的开发者,而MCP服务器则适用于那些在运行时发现工具并读取其描述以决定要调用何种功能的模型。底层API仍然是必需的,MCP只是规范了模型如何查找并调用该API。在确定性应用代码中可直接调用API,而当模型需要在运行时选择操作时,则应使用MCP。
MCP与智能体。二者运行于不同的层级,因此将它们进行比较属于分类错误。智能体是负责推理、规划与决策的部分;而MCP则是它与外部世界连接的通道。没有MCP,智能体只能思考却无法行动;而没有智能体的MCP服务器则没有调用者。这里的Claude Code就是智能体,而MCP服务器则充当它的“手”。
RAG与微调的对比。两者虽途径不同,但都会改变模型的能力。微调通过调整权重来教导模型特定的行为模式:语调、格式、领域风格以及一致的结构。这一过程成本高昂且迭代缓慢,要更新知识还需重新训练模型。而RAG则不改动模型权重,仅通过调整提示词来传授事实信息,更新时只需重新索引文档,仅需几秒钟即可完成。通常而言,用微调来设定模型的行为方式,用RAG来决定模型应掌握的知识内容。如果答案会随着文档的变化而改变,那就是RAG在起作用;如果答案会随着风格指南的变化而改变,那可能是微调在起作用。
选择技术组合
无论开发哪一部分,都可以在2026年已然十分拥挤的技术市场中进行选择。
在 RAG 部分,你需要一个向量数据库。Qdrant 可以在 Docker 中轻松自托管,并提供官方 MCP 服务器,因此可以通过脚本或 Claude Code 访问某个索引。Chroma 是最容易入门的选项,无需任何基础设施,几分钟即可运行。Weaviate 和 Pinecone 则是更成熟的方案,其中 Pinecone 为那些不想自行管理任何服务的团队提供了全托管服务。在任务编排方面,LlamaIndex 仍是解决检索与索引问题最专注的框架,而 LangChain 的功能则更为广泛但体积也更大。
在MCP方面,Composio通过单个端点整合众多应用,并按需加载工具,从而避免了因连接数十台服务器而导致的上下文冗余问题。Arcade.dev将身份验证视为核心问题,会在工具执行过程中注入OAuth令牌,同时不将其暴露给模型,这在涉及多用户时尤为重要。Mem0则介于这两者之间:它既是一台MCP服务器,其功能又侧重于语义记忆,也就是信息检索。
接下来是可观测性。当系统的两个部分都在运行时,能够查看哪些检索请求返回了无效数据,哪些工具调用失败了,而开源且可自托管的Langfuse正好能满足这一需求。
值得注意的是,现在已有几家供应商为以信息检索为核心功能的产品提供了MCP服务器。实际上,这两种理念正在趋同,而非相互竞争。
常见问题
RAG与MCP是一样的吗? 不是。RAG是将检索到的文本插入提示词中,以便模型能够读取,而MCP则让模型能够调用工具并执行操作。前者让模型拥有更多信息,后者则让模型具备更多功能。二者作用于不同层面,但常常在同一应用中共同出现。
MCP会取代RAG吗? 不会,这样认为会导致实际设计错误。MCP仅标准化了工具的调用方式。如果需要对50,000份文档进行语义搜索,仍然需要嵌入模型、向量数据库以及检索流程。MCP只是改变了访问路径,使得该流程变成标准接口背后的一个工具,而非定制化的集成方案。
何时RAG是更好的选择?当答案存在于大量且相对稳定的文本之中,比如文档、合同、政策、研究论文或支持文章时。与通过API往返查询相比,它的每次查询成本更低,响应速度也更快。一旦答案依赖于当前状态,或者用户希望得到结果而非解释,就应该立即转而使用MCP。
MCP服务器与API有区别吗?通常MCP是API之上的一层架构。API代表的是功能本身,而MCP则是模型发现并调用这些功能的标准方式。开发人员在构建系统时会阅读API文档,而模型在运行时则会读取MCP工具描述。
MCP需要向量数据库吗?不需要。MCP本身没有检索功能。只有当你提供的工具需要进行语义搜索时,才需要向量数据库,而且它位于该工具的后面,而非旁边。
MCP服务器能否在内部运行RAG?可以,这是常见的生产模式。构建一个常规的检索流程,并将其作为一个工具暴露出来,比如命名为search_internal_docs。模型只需调用这个工具,具体的嵌入生成和向量搜索工作则在背后完成。
哪种方式成本更低?一旦完成索引,RAG在每次查询时的成本通常更低:只需一次小的嵌入生成调用加上一次向量检索。而MCP的成本则取决于其背后的API成本,从获取股票报价的较低成本到处理浏览器会话或文档的较高成本不等。最浪费成本的错误做法是将那些本可以通过静态索引处理的问题发送给MCP。
MCP与A2A有何关联?它们解决的是不同的问题。MCP负责将模型与工具连接起来,而A2A(智能体间交互)则让各个智能体能够相互沟通。同一个智能体可以通过MCP调用数据库,再通过A2A将任务交给专门的智能体处理,这样两者就能协同工作而非相互竞争。
如何快速做出选择?想象一下,如果需要在一小时内重新执行该查询:如果结果会有所不同,那就属于实时数据,应使用MCP;如果结果保持不变,那就是稳定知识,应使用RAG。之后还要判断用户是否希望触发某些操作,因为无论如何都需要MCP——RAG无法主动执行操作。
为何混淆依然存在
这种比较之所以不断出现,是因为这两种技术大致在同一时间流行起来,而且人们都用“让人工智能能够访问你的数据”这样的表述来描述它们。但这种表述的含金量实在过高。能够读取已写的内容与能够在系统内部执行操作是两码事。只要正确区分这两者,架构基本上就能自行确定。
相关阅读
- 逐层解析:为人工智能代理设计真正的RAG检索管道 — 通过SourceDocument和DocumentChunk合约、分块处理、嵌入技术以及ChromaDB来规划RAG管道,所有这些功能都由RetrievalService统一管理,从而确保代理的稳定性。