day28-McpAgent+MCP和RAG理论
Agents SDK+MCP智能体开发
MCP入门介绍与接入Agents SDK基本流程
2025年3月27号,Agents SDK正式官宣支持MCP使用,这也使得Agents SDK的实际应用场景得到拓展:
现在,我们仅需在创建Agent的时候,将MCP服务器视作为一项工具,即可顺利调用MCP服务器进行Agent开发。而实际在借助Agents SDK调用MCP的流程也非常简单,我们只需将MCP视作tools,即可进行调用。换而言之,就是如果使用Agents SDK作为Agent开发框架,则可以零门槛快速接入MCP海量服务器生态。
MCP+Agents SDK基础调用流程(stdio模式)
环境介绍
程序需要依附于openai-agents-sdk和mcp的双重环境。因此我们可以选择进入到agents-sdk对应的虚拟环境中,然后下载安装mcp的依赖环境。
- pip install uv
- pip install mcp
- pip install https
在新版的Agents SDK中,Agents SDK可以将某个对应的Agent封装为client与外部定义好的server进行通信。基本实现流程如下,还是查询天气的server.py,现在将其复制到jupyter运行主目录下,并修改名称为weather_server.py:(mcp的server端脚本程序)
server.py程序如下:
#weather_server.py
import json
import httpx
from typing import Any
from mcp.server.fastmcp import FastMCP
# 初始化 MCP 服务器
mcp = FastMCP("WeatherServer")
# OpenWeather API 配置
OPENWEATHER_API_BASE = "https://api.openweathermap.org/data/2.5/weather"
API_KEY = "1e6d3a1dc54f267a5a72e5ccab3f615a" # 请替换为你自己的 OpenWeather API Key
USER_AGENT = "weather-app/1.0"
async def fetch_weather(city: str) -> dict[str, Any] | None:
"""
从 OpenWeather API 获取天气信息。
:param city: 城市名称(需使用英文,如 Beijing)
:return: 天气数据字典;若出错返回包含 error 信息的字典
"""
params = {
"q": city,
"appid": API_KEY,
"units": "metric",
"lang": "zh_cn"
}
headers = {"User-Agent": USER_AGENT}
async with httpx.AsyncClient() as client:
try:
response = await client.get(OPENWEATHER_API_BASE, params=params, headers=headers, timeout=30.0)
response.raise_for_status()
return response.json() # 返回字典类型
except httpx.HTTPStatusError as e:
return {"error": f"HTTP 错误: {e.response.status_code}"}
except Exception as e:
return {"error": f"请求失败: {str(e)}"}
def format_weather(data: dict[str, Any] | str) -> str:
"""
将天气数据格式化为易读文本。
:param data: 天气数据(可以是字典或 JSON 字符串)
:return: 格式化后的天气信息字符串
"""
# 如果传入的是字符串,则先转换为字典
if isinstance(data, str):
try:
data = json.loads(data)
except Exception as e:
return f"无法解析天气数据: {e}"
# 如果数据中包含错误信息,直接返回错误提示
if "error" in data:
return f"⚠️ {data['error']}"
# 提取数据时做容错处理
city = data.get("name", "未知")
country = data.get("sys", {}).get("country", "未知")
temp = data.get("main", {}).get("temp", "N/A")
humidity = data.get("main", {}).get("humidity", "N/A")
wind_speed = data.get("wind", {}).get("speed", "N/A")
# weather 可能为空列表,因此用 [0] 前先提供默认字典
weather_list = data.get("weather", [{}])
description = weather_list[0].get("description", "未知")
return (
f"🌍 {city}, {country}\n"
f"🌡 温度: {temp}°C\n"
f"💧 湿度: {humidity}%\n"
f"🌬 风速: {wind_speed} m/s\n"
f"🌤 天气: {description}\n"
)
@mcp.tool()
async def query_weather(city: str) -> str:
"""
输入指定城市的英文名称,返回今日天气查询结果。
:param city: 城市名称(需使用英文)
:return: 格式化后的天气信息
"""
data = await fetch_weather(city)
return format_weather(data)
if __name__ == "__main__":
# 以标准 I/O 方式运行 MCP 服务器
mcp.run(transport='stdio')
创建一个run_agent.py文件:(不可在jupyter下执行,需要在cmd中基于python解释器执行下面程序:python run_agent.py)
from openai import AsyncOpenAI
from agents import OpenAIChatCompletionsModel, Agent, Runner, set_default_openai_client
from agents.mcp import MCPServerStdio, MCPServerStdioParams
import asyncio
import os
import logging
# 完全禁用所有日志
logging.getLogger().setLevel(logging.CRITICAL)
# 自定义模型对象
external_client = AsyncOpenAI(
base_url="https://api.deepseek.com",
api_key="xxxx",
)
# 设置默认模型
set_default_openai_client(external_client)
# 创建模型对象
deepseek_model = OpenAIChatCompletionsModel(
model="deepseek-chat",
openai_client=external_client
)
# 定义mcp连接和agent调用函数
async def run_with_mcp_detailed():
"""详细调试 MCP 连接过程"""
try:
print("步骤1: 创建 MCP Server的连接参数...")
stdio_params = MCPServerStdioParams(
command="python",
#mcp server对应的文件路径
args=["weather_server.py"]
)
print("✓ 参数创建成功")
print("步骤2: 创建 MCP Server 实例对象...")
mcp_server = MCPServerStdio(
params=stdio_params,
#mcp server的自定义名称(叫啥都行)
name="Weather Server",
cache_tools_list=True
)
print("✓ MCPServerStdio 实例创建成功")
print("步骤3: 连接mcp server...")
async with mcp_server as server:
print("✓ MCP 服务器连接成功")
print("步骤4: 创建 Agent...")
agent = Agent(
name="Assistant",
instructions="你是一名助人为乐的助手,可以使用天气查询工具帮助用户查询天气。请使用get_weather工具来查询天气信息。",
mcp_servers=[server], #指定给当前agent绑定的mcp server(外部工具)
model=deepseek_model
)
print("✓ Agent 创建成功")
print("步骤5: 运行agent...")
message = "请帮我查询北京今天天气如何?"
print(f"用户提问: {message}")
result = await Runner.run(starting_agent=agent, input=message)
print(f"助手回复: {result.final_output}")
except Exception as e:
print(f"❌ 错误发生在: {e}")
import traceback
traceback.print_exc()
# 运行
if __name__ == "__main__":
# 运行主程序
asyncio.run(run_with_mcp_detailed())
上述代码执行:在cmd中进入到了对应的开发环境中,基于python解释器执行上述py文件。python run_agent.py
Agents SDK接入多个MCP服务器流程
如何将Agents SDK同时接入多个MCP服务器,理论上,MCP一个服务器能同时运行多个外部函数,而一个MCP Client则可以连接多个MCP服务器。
这里我们尝试创建一个“写入本地文档”和“天气查询”的MCP服务器:write_server.py和weather_server.py,并将其放在Jupyter主目录下。代码如下:
#write_server.py
import json
import httpx
from typing import Any
from mcp.server.fastmcp import FastMCP
# 初始化 MCP 服务器
mcp = FastMCP("WriteServer")
USER_AGENT = "write-app/1.0"
@mcp.tool()
async def write_file(content: str) -> str:
"""
将指定内容写入本地文件。
:param content: 必要参数,字符串类型,用于表示需要写入文档的具体内容。
:return:是否成功写入
"""
return "已成功写入本地文件。"
if __name__ == "__main__":
# 以标准 I/O 方式运行 MCP 服务器
mcp.run(transport='stdio')
#weather_server.py
上述笔记中有该代码,直接复制粘贴即可!
同时调用多个server的py文件(在cmd中基于pyhon解释器执行代码):
from openai import AsyncOpenAI
from agents import OpenAIChatCompletionsModel, Agent, Runner, set_default_openai_client
from agents.mcp import MCPServerStdio, MCPServerStdioParams
import asyncio
import os
import logging
# 完全禁用所有日志
logging.getLogger().setLevel(logging.CRITICAL)
# 自定义模型对象
external_client = AsyncOpenAI(
base_url="https://api.deepseek.com",
api_key="sk-4b79f3a3ff334a15a1935366ebb425b3",
)
# 设置默认模型
set_default_openai_client(external_client)
# 创建模型对象
deepseek_model = OpenAIChatCompletionsModel(
model="deepseek-chat",
openai_client=external_client
)
# 定义mcp连接和agent调用函数
async def run_with_mcp_detailed():
"""详细调试 MCP 连接过程"""
try:
print("步骤1: 创建 MCP服务器参数...")
# 创建天气服务器参数
weather_params = MCPServerStdioParams(
command="python",
args=["weather_server.py"]
)
# 创建写作服务器参数
write_params = MCPServerStdioParams(
command="python",
args=["write_server.py"]
)
print("✓ 参数创建成功")
print("步骤2: 创建 MCP服务器实例...")
weather_server = MCPServerStdio(
params=weather_params,
name="Weather Server",
cache_tools_list=True
)
write_server = MCPServerStdio(
params=write_params,
name="Write Server",
cache_tools_list=True
)
print("✓ MCPServerStdio 实例创建成功")
print("步骤3: 同时连接两个 MCP 服务器...")
async with weather_server as ws, write_server as wrs:
print("✓ 两个 MCP 服务器连接成功")
print("步骤4: 创建 Agent(集成两个服务器的工具)...")
agent = Agent(
name="MultiTool Assistant",
instructions="""你是一个多功能助手,可以同时使用天气查询和本地文档写入的能力。
你可以使用的工具包括:
1. 天气查询工具 - 查询全球城市的天气信息
2. 本地文档写入 - 帮助用户将相关数据写入本地文档进行保存
请根据用户的需求智能选择合适的工具:
- 当用户询问天气时,使用天气查询工具
- 当用户需要写入数据保存时,使用本地文档写入工具
- 如果用户的需求涉及多个方面,可以组合使用多个工具
- 如果不知道使用哪个工具,可以询问用户澄清""",
mcp_servers=[ws, wrs], # 传入两个服务器
model=deepseek_model
)
print("✓ Agent 创建成功")
print("步骤5: 运行查询...")
message = "请帮我查询北京天气,并写入本地文档。"
print(f"用户提问: {message}")
result = await Runner.run(starting_agent=agent, input=message)
print(f"助手回复: {result.final_output}")
except Exception as e:
print(f"❌ 错误发生在: {e}")
import traceback
traceback.print_exc()
# 运行
if __name__ == "__main__":
# 运行主程序
asyncio.run(run_with_mcp_detailed())
MCP+Agents SDK基础调用流程(SSE模式)
以12306的MCP 服务器应用为例:
from openai import AsyncOpenAI
from agents import OpenAIChatCompletionsModel, Agent, Runner, set_default_openai_client
import asyncio
import os
import logging
from agents.mcp import MCPServerSse
# 完全禁用所有日志
logging.getLogger().setLevel(logging.CRITICAL)
# 自定义模型对象
external_client = AsyncOpenAI(
base_url="https://api.deepseek.com",
api_key="xxxxxx",
)
# 设置默认模型
set_default_openai_client(external_client)
# 创建模型对象
deepseek_model = OpenAIChatCompletionsModel(
model="deepseek-chat",
openai_client=external_client
)
# 定义mcp连接和agent调用函数
async def run_with_sse():
"""使用 SSE 传输方式"""
try:#创建sse模式下的mcp server对象
async with MCPServerSse(
name="Weather Server",
params={
#url就是mcp server配置信息中的链接地址
"url": "https://mcp.api-inference.modelscope.net/fd89f5e217084f/sse"
}
) as server:
agent = Agent(
name="Assistant",
instructions="你是一名车票查询的助手",
mcp_servers=[server],
model=deepseek_model
)
message = "帮我查询今天下午北京到上海的高铁余票信息"
print(f"用户提问: {message}")
result = await Runner.run(starting_agent=agent, input=message)
print(f"助手回复: {result.final_output}")
except Exception as e:
print(f"SSE 错误: {e}")
# 运行
if __name__ == "__main__":
# 运行主程序
asyncio.run(run_with_sse())
RAG
一、RAG技术介绍
RAG,Retrieval-Augmented Generation,也被称作检索增强生成技术,最早在 Facebook AI(Meta AI)在 2020 年发表的论文《Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks》中正式提出,这种方法的核心思想是借助一些文本检索策略,让大模型每次问答前都带入相关文本,以此来改善大模型回答时的准确性。这项技术刚发布时并未引发太大关注,而伴随2022年大模型技术大爆发,RAG技术才逐渐进入人们视野,并且由于早期大模型技术应用均已“知识库问答”为主,而RAG技术是最易上手、并且上限极高的技术,因此很快就成为了大模型技术人必备的技术之一。
1.RAG技术实现原理
时至今日,RAG技术已经是非常庞大的技术体系了,从简单的文档切分、存储、匹配,再到复杂的入GraphRAG(基于知识图谱的检索增强),以及复杂文档解析+多模态识别技术等等等等。
而对于初学者来说,为了更好的上手学习RAG技术,我们首先需要对RAG技术最简单的实现形式有个基础的了解。
一个最简单的RAG技术实现流程如下所示:
假设现在我们有一个偌大的知识库,当想从该知识库中去检索最相关的内容时,最简单的方法是:接收到一个查询(Query),就直接在知识库中进行搜索。这种做法其实是可行的,但存在两个关键的问题:
- 假设提问的Query的答案出现在一篇文章中,去知识库中找到一篇与用户输入相关的文章是很容易的,但是我们将检索到的这整篇文章直接放入
Prompt中并不是最优的选择,因为其中一定会包含非常多无关的信息,而无效信息越多,对大模型后续的推理影响越大。 - 任何一个大模型都存在最大输入的Token限制,一个流程中可能涉及多次检索,每次检索都会产生相应的上下文,无法容纳如此多的信息。

解决上述两个问题的方式是:把存放着原始数据的知识库(Knowledge)中的每一个raw data,切分成一个一个的小块,这些小块可以是一个段落,也可以是数据库中某个索引对应的值。这个切分过程被称为“分块”(chunking),如下述流程所示:

以第一个原始数据为例(raw data 1),通过一些特定的方法进行切分,一个完整的内容会被分割成 chunk1 ~ chunk4。采取相同的方法,继续对raw data 2、raw data 3直至raw data n进行切分。完成这一过程后,我们最终得到的是一个充满分块数据(chunks)的新的知识库(repository),其中每一项都是一个单独的chunk。例如,如果原始文档共有10个,那么经过切分,可能会产生出100个chunks。
完成这一转化后,当再次接收到一个查询(Query)时,就会在更新后的知识库(repository)中进行搜索,这时检索的范围就不再是某个完整的文档,而是其中的某一个部分,返回的是一个或多个特定的chunk,这样返回的信息量就会更小且更精确。随后,这些被检索到的chunk会被加入到Prompt中,作为上下文信息与用户原始的Query共同输入到大模型进行处理,以生成最终的回答。
在上述将原始数据(raw data)转化为chunk的过程中,就会包含构建RAG的第一部分开发工作:这包括如果做数据清洗,如去除停用词、标点符号等。此外,还涉及如何选择合适的split方法来进行数据切分的一系列技术。
接下来面临的问题是,尽管所有数据已经被切割成一个个chunk,其存储形式还是以字符串形式存在,如果想从repository中匹配到与输入的query相关的chunks,比较两句话是否相似,看一句话中相同字有几个,这显然是行不通的。我们需要获取的是句子所蕴含的深层含义,而非仅仅是表面的字面相似度。因此,大家也能想到,在NLP中去计算文本相似度的有效的方法就是Embedding,即将这些chunks转换成向量(vector)形式。所以流程会丰富如下:

Embedding 是由向量模型(⽣成的,它会根据不同的算法⽣成⾼维度的向量数据,代表着数据的不同特征,这些特征代表了数据的不同维度。例如,对于⽂本,这些特征可能包括词汇、语法、语义、情感、情绪、主题、上下⽂等。对于⾳频,这些特征可能包括⾳调、节奏、⾳⾼、⾳⾊、⾳量、语⾳、⾳乐等。
在这个流程中,会先将用户输入的 Query 转化成 Vector,然后再去与知识库中的向量进行相似度比较,检索出相似的Vector,最终返回其对应的Chunk(字符串形式的文本),再执行后续的流程。所以在这个过程中,就会产生构建RAG的第二部分的开发工作:如果将chunk转化成Vector及以何种形式进行存储。同时,我们要考虑的是:如何去计算向量之间的相似度?如果去和知识库中的向量一个一个比较,这个时间复杂度是非常高的,那么其解决办法又是什么呢?我们继续看下述流程:
如上所示,解决搜索效率和计算相似度优化算法的答案就是:向量数据库。同时也产生了构建RAG的第三部分工作:我们要去了解和学习如何选择、使用向量数据库。
最终整体流程就如上图所示,一个基础的RAG架构会只要包含以下几方面的开发工作:
- 如何将原始数据转化成chunks;
- 如何将chunks转化成Vector;
- 如何选择计算向量相似度的算法;
- 如何利用向量数据库提升搜索效率;
- 如何把找到的chunks与原始query拼接在一起,产生最终的Prompt;
在以上5点开发任务中,我们确实是可以利用已经训练好的Embedding模型,开源的向量数据库等去直接解决某一类问题,所以我们前面才说一个基础架构的RAG系统搭建起来其实很简单,但搭建并不意味着直接就能用,毕竟RAG的核心是检索,检索出来的内容的准确率是衡量一个RAG系统的最基础的标准。目前没有任何一套理论、任何一套解决方案能够百分之百的指导着我们构建出一个最优的RAG系统。不同的需求,不同的数据,其构建方法也会大相径庭,需要我们在实践的过程中不断地去尝试,不断地去积累相关的经验,才能够将其真正落地。
我们需要围绕给定的文档(往往是非常长的文档)先进行切分,然后将切分的文档转化为计算机能识别的形式,也就是将其转化为一个数值型向量(也被称为词向量),然后当用户询问问题的时候,我们再将用户的问题转化为词向量,并和段落文档的词向量进行相似度匹配,借此找出和当前用户问题最相关的原始文档片段,然后将用户的问题和匹配的到的原文片段都带入大模型,进行最终的问答。由此便可实现一次完整的文档检索增强执行流程。
具体执行过程如下所示:
2 相关核心概念和操作
2.1 向量数据库
向量数据库(Vector Database),也叫矢量数据库,主要用来存储和处理向量数据。
在数学中,向量是有大小和方向的量,可以使用带箭头的线段表示,箭头指向即为向量的方向,线段的长度表示向量的大小。两个向量的距离或者相似性可以通过欧式距离或者余弦距离得到。
图像、文本和音视频这种非结构化数据都可以通过某种变换或者嵌入学习转化为向量数据存储到向量数据库中,从而实现对图像、文本和音视频的相似性搜索和检索。这意味着您可以使用向量数据库根据语义或上下文含义查找最相似或相关的数据。
向量数据库的主要特点是高效存储与检索。利用索引技术和向量检索算法能实现高维大数据下的快速响应。
2.2 向量嵌入Vector Embeddings
对于传统数据库,搜索功能都是基于不同的索引方式加上精确匹配和排序算法等实现的。本质还是基于文本的精确匹配,这种索引和搜索算法对于关键字的搜索功能非常合适,但对于语义搜索功能就非常弱。
例如,如果你搜索 “小狗”,那么你只能得到带有“小狗” 关键字相关的结果,而无法得到 “柯基”、“金毛” 等结果,因为 “小狗” 和“金毛”是不同的词,传统数据库无法识别它们的语义关系,所以传统的应用需要人为的将 “小狗” 和“金毛”等词之间打上小狗特征标签进行关联,这样才能实现语义搜索。
同样,当你在处理非结构化数据时,你会发现非结构化数据的特征数量会迅速增加,处理过程会变得十分困难。比如我们处理图像、音频、视频等类型的数据时,这种情况尤为明显。就拿图像来说,可以标注的特征包括颜色、形状、纹理、边缘、对象、场景等多个方面。然而,这些特征数量众多,而且依靠人工进行标注的难度很大。因此,我们需要一种自动化的方式来提取这些特征,而Vector Embedding技术就能够实现这一目标。
Vector Embedding 是由专门的向量模型生成的,它会根据不同的算法生成高维度的向量数据,代表着数据的不同特征,这些特征代表了数据的不同维度。例如,对于文本,这些特征可能包括词汇、语法、语义、情感、情绪、主题、上下文等。对于音频,这些特征可能包括音调、节奏、音高、音色、音量、语音、音乐等。
2.3 相似性测量
如何衡量向量之间的相似性呢?有三种常见的向量相似度算法:欧几里德距离、余弦相似度和点积。
- 点积(内积): 两个向量的点积是一种衡量它们在同一方向上投影的大小的方法。如果两个向量是单位向量(长度为1),它们的点积等于它们之间夹角的余弦值。因此,点积经常被用来计算两个向量的相似度。
- 余弦相似度: 这是一种通过测量两个向量之间的角度来确定它们相似度的方法。余弦相似度是两个向量点积和它们各自长度乘积的商。这个值的范围从-1到1,其中1表示完全相同的方向,-1表示完全相反,0表示正交。
- 欧氏距离: 这种方法测量的是两个向量在n维空间中的实际距离。虽然它通常用于计算不相似度(即距离越大,不相似度越高),但可以通过某些转换(如取反数或用最大距离归一化)将其用于相似度计算。
像我们最常用的余弦相似度,其代码实现也非常简单,如下所示:
import numpy as np
def cosine_similarity(A, B):
# 使用numpy的dot函数计算两个数组的点积
# 点积是向量A和向量B在相同维度上对应元素乘积的和
dot_product = np.dot(A, B)
# 计算向量A的欧几里得范数(长度)
# linalg.norm默认计算2-范数,即向量的长度
norm_A = np.linalg.norm(A)
# 计算向量B的欧几里得范数(长度)
norm_B = np.linalg.norm(B)
# 计算余弦相似度
# 余弦相似度定义为向量点积与向量范数乘积的比值
# 这个比值表示了两个向量在n维空间中的夹角的余弦值
return dot_product / (norm_A * norm_B)
2.4 相似性搜素
既然我们知道了可以通过比较向量之间的距离来判断它们的相似度,那么如何将它应用到真实的场景中呢?如果想要在一个海量的数据中找到和某个向量最相似的向量,我们需要对数据库中的每个向量进行一次比较计算,但这样的计算量是非常巨大的,所以我们需要一种高效的算法来解决这个问题。
高效的搜索算法有很多,其主要思想是通过两种方式提高搜索效率:
1)减少向量大小——通过降维或减少表示向量值的长度。
2)缩小搜索范围——可以通过聚类或将向量组织成基于树形、图形结构来实现,并限制搜索范围仅在最接近的簇中进行。
我们首先来介绍⼀下大部分算法共有的核心概念,也就是kmeans聚类。
K-Means聚类
我们可以在保存向量数据后,先对向量数据先进行聚类。例如下图在二维坐标系中,划定了 4 个聚类中心,然后将每个向量分配到最近的聚类中心,经过聚类算法不断调整聚类中心位置,这样就可以将向量数据分成 4 个簇。每次搜索时,只需要先判断搜索向量属于哪个簇,然后再在这一个簇中进行搜索,这样就从 4 个簇的搜索范围减少到了 1 个簇,大大减少了搜索的范围。
HNSW
除了聚类以外,也可以通过构建树或者构建图的方式来实现近似最近邻搜索。这种方法的基本思想是每次将向量加到数据库中的时候,就先找到与它最相邻的向量,然后将它们连接起来,这样就构成了一个图。当需要搜索的时候,就可以从图中的某个节点开始,不断的进行最相邻搜索和最短路径计算,直到找到最相似的向量。
2.5 Embedding models
对于Embedding Models我们只需要学会如何去使用就可以,是因为有非常多的模型供应商,如OpenAI、Hugging Face国内的有百川、千帆都提供了标准接口并集成在LangChian框架中,这意味着:Embedding Models已经有人帮我们训练好了,我们只要按照其提供的接口规范,将自然语言文本传入进去,就能得到其对应的向量表示。这显然是非常简单的。
如何使用Baichuan Text Embeddings
要使用相关的Embedding模型,首先需要获取API密钥。您可以通过以下步骤获取:
- 访问百川智能官方网站或者[阿里百炼][https://bailian.console.aliyun.com/?spm=5176.29597918.nav-v2-dropdown-menu-0.d_main_2_0.2abf7b08IPY2jd&scm=20140722.M_10838657._.V_1#/model-market/detail/text-embedding-v3]
- 注册并创建一个账户
- 在控制台中申请并获取API密钥
2.6 代码构建简易RAG
下节课讲
3.RAG核心作用
那RAG这样的一个检索增强流程到底有什么用呢?这就不得不从当代大模型本身的三项技术缺陷开始说起了。
缺陷一:大模型幻觉
相信大家在使用大模型的时候,都会遇到大模型无中生有胡编乱造答案的情况,这就是所谓的大模型幻觉。而其中,第一代DeepSeek R1模型的幻觉是非常严重的,平均七次回答中就会有一次的回答存在幻觉,这可以说是第一版R1模型最大的短板。
而大型语言模型之所以会产生幻觉,主要是因为它们的训练方式和内在机制决定了它们并不具备真正理解和验证事实的能力。模型在训练过程中,通过分析大规模文本数据来学习不同词语和句子之间的概率关系,也就是在某种程度上掌握“在什么上下文中,什么样的回答听起来更合理”。然而,模型并没有接入实时的知识库或事实核查工具,当它遇到陌生的问题、模糊的描述或者上下文不完整的输入时,就会基于概率和语料库中似是而非的关联去“编造”一个看似正确的答案。由于这些输出往往语法流畅、逻辑连贯,人类读者很容易误以为它是真实可信的内容,这就是我们通常说的“模型幻觉”。
缺陷二:有限的最大上下文
而除此之外,大模型在实际应用中还会另一个“障碍”,那就是最大上下文限制。由于大模型的本质其实是一个算法,不管是让大模型“知道”有哪些外部工具,还是要给大模型进行“背景设置”,或者是要给模型添加历史对话消息,以及本次对话的输出,都需要占用这个上下文窗口。这就使得我们在一次对话中能够给大模型灌输的知识(文本)其实是有限的。
大型语言模型的最大上下文限制,是由它们的架构和计算方式决定的。每次生成回答时,模型需要把输入文本转换成固定长度的数字序列(称为token),并在内部一次性加载到模型的“上下文窗口”中进行处理。这个窗口的大小是有限的,不同模型一般在几千到几万token之间。如果输入内容超出这个长度,模型要么截断最前面的部分,要么丢弃部分信息,这就会造成对话历史、长文档或先前提到的重要细节的遗失。因为它无法跨越上下文窗口无限地保留信息,所以在面对长对话或者大量背景知识时,模型常常出现上下文断裂、回答不连贯或者忽略先前条件的情况。
早些时候的大模型普遍是8k最大上下文,相当于是8-10页中文PDF,伴随着大模型预训练技术的不断发展,顶尖的大模型,如Gemini 2.5 Pro和GPT-4.1等模型,已经达到了1M的最大上下文长度,相当于是一千页的PDF,相当于1.5本《红楼梦》,而普通的模型,也基本达到64K或128K最大上下文,相当于60-100也左右的PDF。
但是,模型上下文的增长也是有限度的,对于开发者来说,能够一次性输入的信息都会有限制。
缺陷三:模型专业知识与时效性知识不足
大型语言模型虽然在通用领域展现出令人瞩目的语言理解和生成能力,但其在特定领域的专业知识掌握往往存在明显局限。其根本原因在于,模型的训练依赖于预先收集的大规模语料,这些语料覆盖面虽广,却很难保证在所有专业领域中具有足够的深度和准确性。
某些领域,如医学、法律或前沿科技,知识更新速度快且门槛较高,公开可获取的高质量数据本身就有限,模型难以在此基础上形成系统性和权威性的认知。此外,模型训练通常在固定的时间点结束,因此其所掌握的知识具有天然的时效性,无法实时反映新近出现的研究成果、政策变化或行业动态。这种静态的知识存储模式,决定了大模型在面对最新或高度专业化的问题时,往往难以提供全面、精确的解答。
基于此,我们再回顾RAG的技术实现流程,就不难发现其背后的技术价值了:如果我们能在每次对话的时候,为当前模型输入最精准的问题相关的文档,那就能拓展模型的知识边界,无论是提升模型专业知识的准确性、给模型灌输一些时效性的知识、或者消除模型幻觉,都将大有助益,而在其他一些对话场景中,无论是需要围绕海量的文本搭建本地问答知识库、还是在构建无限上下文的聊天机器人,RAG技术都是最佳解决方案。

浙公网安备 33010602011771号