在大语言模型(LLM)的实际应用中,单靠模型自身的能力会遇到严重的瓶颈。RAG(检索增强生成)技术的出现,正是为了解决原生LLM在商业和技术落地上的五大硬伤:
- 根治LLM的幻觉 硬伤大语言模型本质上是基于概率的下一个词预测生成器 :
- 没有RAG:当模型遇到不掌握的知识时,它不会说我不知道 ,而是会一本正经地胡编乱造,产生严重的幻觉(Hallucination);
- 引入RAG:它强迫LLM进行开卷考试。模型必须基于检索到的确定性参考资料来组织语言,从根本上压制了胡思乱想,确保回答的严谨性;
- 跨越知识时效性的时间墙 所有LLM在训练完成的那一刻,其内部知识就已经冻结了:
- 没有RAG:模型无法得知今天早上刚发生的新闻,也无法得知你刚刚更新的产品规格。如果想要让它知道,必须重新花费数周时间、数百万资金去**微调(Fine-tuning)**或重新训练模型;
- 引入RAG:无需触动模型本身。只需要把最新的新闻、实时的股市数据、或今天的会议记录放入外部知识库,LLM通过即时检索,就能立刻掌握最新动态;
- 解锁企业私有海量数据 LLM是在公开的互联网数据上训练出来的,对企业内部的机密知识 一无所知:
- 没有RAG:企业如果直接把自己的财务报表、员工手册、客户服务记录喂给公共LLM去训练,会导致严重的商业机密泄露风险;
- 引入RAG:企业的数据可以安全地保存在本地或私有云的向量数据库中。只有在用户提问时,系统才会安全地检索相关片段提供给AI临时阅读,完美保护了数据隐私;
- 实现答案的可追溯性 与透明度原生LLM的输出是一个黑盒,你很难知道它的某句话是从哪里学来的:
- 没有RAG:AI 给出医疗建议或法律条款时,用户无法判断其真伪,不敢盲目采信;
- 引入RAG:由于LLM是根据检索到的具体文本片段进行回答的,系统可以轻松地在回答末尾附带来源引用(例如:"参考自《2026年Q1财务报告》第14页"),从而建立了极高的信任度;
- 极低的管理与算力成本这是RAG能够在企业级应用中迅速普及的商业决定性原因:
- 对比模型微调(SFT):微调需要昂贵的GPU算力、专业的 AI 科学家、以及繁琐的数据标注,且每次更新数据都要重新来过;
- RAG的优势:只需像管理网盘一样,往数据库里增删改文档(增量更新),成本几乎为零。普通开发人员只需几行代码即可搭建完成;
以下面这个程序为例。我们通过LangGraph SDK来调用之前构建的Agent服务器,并请求它回答**2026年斯诺克世界赛冠军是谁?**这个问题。由于这个问题涉及到2026年的最新信息,原生的LLM是无法回答的(会产生幻觉),但是由于我们在Agent服务器端引入了RAG技术,Agent可以通过检索最新的知识库来给出正确的答案。
csharp
using Azure.AI.Projects;
using dotenv.net;
using Microsoft.Extensions.AI;
using OpenAI;
using OpenAI.Responses;
using System.ClientModel;
DotEnv.Load();
var model = Environment.GetEnvironmentVariable("MODEL")!;
var apiKey = Environment.GetEnvironmentVariable("API_KEY")!;
var endpoint = Environment.GetEnvironmentVariable("OPENAI_URL")!;
var agent = new OpenAIClient(
credential: new ApiKeyCredential(key: apiKey),
options: new OpenAIClientOptions { Endpoint = new Uri(endpoint) })
.GetResponsesClient()
.AsAIAgent(model: model);
var response = await agent.RunAsync(message:"2026年斯诺克世界赛冠军是谁?");
Console.WriteLine(response.Text);
输出:
markdown
我目前**无法确定**2026年斯诺克世界锦标赛的冠军是谁。
原因是:
- 虽然按赛程来看,2026年世锦赛通常在**4月下旬至5月初**结束,但
- 我无法获取**实时或最新比赛结果**,而且不应在没有可靠数据的情况下猜测冠军。
你可以选择下面两种方式之一:
1. **如果你想要的是已产生的正式结果**:我可以告诉你去哪里快速核实(如世界斯诺克官网、BBC Sport、世界斯诺克巡回赛官方微博等)。
2. **如果你想要的是赛前或赛中的预测/分析**:我可以根据当时的世界排名、签表、球员状态,给你一个专业预测。
你是想问**"已经夺冠的是谁"**,还是**"你预测谁会夺冠"**?
由于模型训练的时间早于2026年,所以它无法知道2026年斯诺克世界锦标赛的冠军是谁。既然模型不均有相关的知识,我们就根据用户的查询将相关的信息检索出来,作为上下文提供给LLM来生成回答。接下来我们通过简单例子看看在LangChain和MAF中如何利用RAG来解决这个问题。
1. LangChain
传统的RAG基本建立在向量数据库基于自然语言的检索上。简单起见,下面的程序程序将检索逻辑硬编码在工具函数search_context中。在这个模拟基于用户查询检索的工具函数中,如果用户的查询中同时包含2026 、斯诺克 和世锦赛 这几个关键词,我们就返回一段相关的新闻稿。除了提供文本内容之外,我们还附带了一个SourceLink和SourceName来说明这个信息的来源。
python
from langchain.agents import create_agent
from langchain.tools import tool
from dotenv import load_dotenv
load_dotenv()
@tool
def search_context(query:str) -> str:
"""根据用户的查询检索相关的上下文信息"""
if "2026" in query and "斯诺克" in query and "世锦赛" in query:
news = """\
北京时间2026年5月5日,英国谢菲尔德克鲁斯堡剧院,决胜局最后一颗黑球落袋后,00后中国球员吴宜泽挥拳庆祝。18:17,吴宜泽击败肖恩·墨菲,拿下2026年斯诺克世锦赛冠军。
社交平台上,"吴宜泽夺冠"迅速登上热搜,不少球迷将这场胜利形容为"中国斯诺克新的接力时刻"。这是继2025年赵心童夺冠之后,中国选手再次问鼎这项赛事最高荣誉。这也是在经历2023年前后相关禁赛与争议事件后,中国球员重新回到世界顶级竞争序列的重要节点。
相比十多年前,丁俊晖在斯诺克领域的单点突破,中国近几年开始稳定涌现世界级斯诺克选手。从个体突破到群体崛起,这项运动在中国已经进入新的发展阶段。
""";
return f"""\
SourceLink: https://baijiahao.baidu.com/s?id=1864605028122769594
SourceName: 每日经济新闻,
Text: {news}
"""
return "No relevant context found."
agent = create_agent(
model= "gpt-5.2-chat",
tools=[search_context],
system_prompt="你拥有一个根据用户查询检索相关上下文信息的工具`search_context`。请根据用户的查询调用工具获取相关的上下文信息,并结合这些信息给出最终的回答。"
)
result = agent.invoke(input={
"messages":[{"role":"user", "content": "2026年斯诺克世锦赛的冠军是谁?"}]
})
for message in result["messages"]:
message.pretty_print()
我们在系统提示词中明确告诉模型它拥有一个名为search_context的工具,这个工具可以根据用户的查询来检索相关的上下文信息。模型需要先调用这个工具来获取相关的信息,然后才能结合这些信息来生成最终的回答。程序输出结果如下:
markdown
================================ Human Message =================================
2026年斯诺克世锦赛的冠军是谁?
================================== Ai Message ==================================
Tool Calls:
search_context (call_fEqvdSEywsm9aCrJAeJJdQaO)
Call ID: call_fEqvdSEywsm9aCrJAeJJdQaO
Args:
query: 2026年 斯诺克 世锦赛 冠军
================================= Tool Message =================================
Name: search_context
SourceLink: https://baijiahao.baidu.com/s?id=1864605028122769594
SourceName: 每日经济新闻,
Text: 北京时间2026年5月5日,英国谢菲尔德克鲁斯堡剧院,决胜局最后一颗黑球落袋后,00后中国球员吴宜泽挥拳庆祝。18:17,吴宜泽击败肖恩·墨菲,拿下2026年斯诺克世锦赛冠军。
社交平台上,"吴宜泽夺冠"迅速登上热搜,不少球迷将这场胜利形容为"中国斯诺克新的接力时刻"。这是继2025年赵心童夺冠之后,中国选手再次问鼎这项赛事最高荣誉。这也是在经历2023年前后相关禁赛与争议事件后,中国球员重新回到世界顶级竞争序列的重要节点。
相比十多年前,丁俊晖在斯诺克领域的单点突破,中国近几年开始稳定涌现世界级斯诺克选手。从个体突破到群体崛起,这项运动在中国已经进入新的发展阶段。
================================== Ai Message ==================================
2026年斯诺克世锦赛的冠军是**吴宜泽**。
他在北京时间 **2026年5月5日** 于英国谢菲尔德克鲁斯堡剧院举行的决赛中,击败了**肖恩·墨菲**,成功夺冠。这也是继2025年赵心童夺冠之后,中国选手连续第二年获得斯诺克世锦赛冠军。
上面这段输出体现包含整个推理流程产生的四个消息:
- Human Message:用户的输入消息,即"2026年斯诺克世锦赛的冠军是谁?"
- Ai Message:模型生成的消息,其中包含了模型调用工具的相关信息(工具名称、调用ID、调用参数等)。
- Tool Message:工具函数search_context的输出结果,其中包含了检索到的相关上下文信息(新闻内容、来源链接和来源名称)。
- Ai Message:模型根据工具函数的输出生成的最终回答,即"2026年斯诺克世锦赛的冠军是吴宜泽"。
关于RAG涉及的内容还有很多,比如各种文档的加载、文本的分割、向量数据库、重排序甚至图数据库(实现GraphRAG)。如果希望了解关于RAG的更多内容,可以参考我们之前的博客文章系列RAG在LangChain中的实现(共8篇)。
2. MAF
顾名思义,RAG的本质上属于AIAgent输入增强的范畴。在ChatClientAgent的管道中,具有专门用于输入和响应增强的组件,那就是AIContextProvider。MAF的常规额RAG结解决方案由TextSearchProvider实现,它就派生于AIContextProvider。
这是上面演示实例针对MAF的实现版本。我们首先创建了一个TextSearchProvider,调用其构造函数提供的第一个参数是一个指向SearchAsync函数的委托,TextSearchProvider正是使用它来完成上下文的检索。接下来我们创建了一个针对OpenAIClient的ChatClientAgent,在调用AsAIAgent扩展方法时,我们提供了一个ChatClientAgentOptions对象,并将之前创建的TextSearchProvider添加到AIContextProviders属性中。
csharp
using Azure.AI.Projects;
using dotenv.net;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OpenAI;
using OpenAI.Responses;
using System.ClientModel;
using static Microsoft.Agents.AI.TextSearchProvider;
DotEnv.Load();
var model = Environment.GetEnvironmentVariable("MODEL")!;
var apiKey = Environment.GetEnvironmentVariable("API_KEY")!;
var endpoint = Environment.GetEnvironmentVariable("OPENAI_URL")!;
var textSearchProvider = new TextSearchProvider(searchAsync: SearchAsync);
var agent = new OpenAIClient(
credential: new ApiKeyCredential(key: apiKey),
options: new OpenAIClientOptions { Endpoint = new Uri(endpoint) })
.GetChatClient(model:model)
.AsIChatClient()
.AsAIAgent(options: new ChatClientAgentOptions { AIContextProviders = [textSearchProvider] });
var response = await agent.RunAsync(message:"2026年斯诺克世界赛冠军是谁?");
Console.WriteLine(response.Text);
static Task<IEnumerable<TextSearchResult>> SearchAsync(string query, CancellationToken cancellationToken)
{
if (query.Contains("2026") && query.Contains("斯诺克") && query.Contains("世界赛"))
{
var news = """
北京时间2026年5月5日,英国谢菲尔德克鲁斯堡剧院,决胜局最后一颗黑球落袋后,00后中国球员吴宜泽挥拳庆祝。18:17,吴宜泽击败肖恩·墨菲,拿下2026年斯诺克世锦赛冠军。
社交平台上,"吴宜泽夺冠"迅速登上热搜,不少球迷将这场胜利形容为"中国斯诺克新的接力时刻"。这是继2025年赵心童夺冠之后,中国选手再次问鼎这项赛事最高荣誉。这也是在经历2023年前后相关禁赛与争议事件后,中国球员重新回到世界顶级竞争序列的重要节点。
相比十多年前,丁俊晖在斯诺克领域的单点突破,中国近几年开始稳定涌现世界级斯诺克选手。从个体突破到群体崛起,这项运动在中国已经进入新的发展阶段。
""";
var result = new TextSearchResult
{
RawRepresentation = news,
SourceLink = "https://baijiahao.baidu.com/s?id=1864605028122769594",
SourceName = "每日经济新闻",
Text = news
};
return Task.FromResult<IEnumerable<TextSearchResult>>([result]);
}
else
{
return Task.FromResult<IEnumerable<TextSearchResult>>([]);
}
}
我们最终调用RunAsync方法来请求Agent回答**2026年斯诺克世界赛冠军是谁?**这个问题。程序输出结果如下:
markdown
2026年斯诺克世界锦标赛冠军是**吴宜泽**。
据《每日经济新闻》报道,北京时间2026年5月5日,在英国谢菲尔德克鲁斯堡剧院举行的决赛中,吴宜泽在决胜局击败肖恩·墨菲,成功夺得2026年斯诺克世锦赛冠军。这也是继2025年赵心童夺冠之后,中国选手再次问鼎这项斯诺克最高荣誉赛事。
来源:每日经济新闻
链接:https://baijiahao.baidu.com/s?id=1864605028122769594
关于TextSearchProvider的设计和实现原理,可以参考我们之前的博客文章TextSearchProvider:在MAF中实现RAG。