HelloAgents:RAG——让 Agent 学会检索知识
HelloAgents:RAG——让 Agent 学会检索知识
一、为什么需要 RAG?
随着 ChatGPT、Claude 等大语言模型的发展,人们越来越习惯向 AI 提问各种问题。
例如:
今天北京天气怎么样? Transformer 是什么时候提出的? 帮我阅读这篇论文。 解释一下公司的内部接口文档。虽然大模型拥有庞大的知识储备,但它并不是万能的。
它存在一个天然缺陷:
模型的知识来源于训练数据,而不是实时互联网。
这意味着:
- 训练完成之后,模型参数基本固定;
- 无法自动学习最新知识;
- 更不知道企业内部的数据。
例如:
公司内部产品说明书 最新 API 文档 实验室资料 PDF Word Markdown 会议纪要这些资料,大模型训练时根本没有见过。
因此,即使模型推理能力再强,也无法回答这些问题。
LLM 的三个局限
HelloAgents 在介绍 RAG 时,也总结了目前 LLM 面临的几个主要问题。
1. 知识具有时效性
例如:
今天最新发布的论文是什么? OpenAI 最近更新了哪些模型?这些内容可能发生在模型训练完成之后。
模型自然无法知道。
2. 无法访问私有知识
例如:
公司的产品文档 学校实验数据 医院病例 企业知识库这些资料不会公开发布。
因此模型无法学习。
3. 容易产生幻觉(Hallucination)
这是目前 LLM 最大的问题之一。
例如:
用户问:
介绍一下 XX 公司新发布的软件。模型没有相关知识。
但是:
它往往不会直接回答:
我不知道。
而是:
根据已有知识进行推测。
最终生成:
看起来很合理,
实际上完全错误。
这种现象就是:
Hallucination(幻觉)。
二、RAG 是什么?
针对这些问题,目前最主流的方法就是:
Retrieval-Augmented Generation(RAG)
中文一般翻译为:
检索增强生成。
它的核心思想其实非常简单:
回答问题之前,不是直接让 LLM 作答,而是先去查资料。
整个流程如下:
用户提问 │ ▼ 检索知识库 │ ▼ 找到相关文档 │ ▼ 加入 Prompt │ ▼ LLM 回答可以发现:
LLM 并没有直接回答问题。
而是先获得一份"参考资料"。
然后再根据资料组织答案。
这种方式就像考试时允许查阅参考书一样。
一个简单例子
假设知识库中保存了:
《Python 深度学习实践》 《机器学习导论》 《Transformer 原理解析》现在用户问:
Transformer 是什么时候提出的?传统 LLM:
直接依靠参数回答。
而 RAG:
首先检索知识库。
找到:
Attention Is All You Need 发表于 2017 年 Google 提出 Transformer。随后把这段内容放入 Prompt:
已检索到资料: Transformer 来源于论文 《Attention Is All You Need》,发表于 2017 年。 请根据以上资料回答用户问题。因此:
最终答案准确率会明显提高。
三、Memory 与 RAG 的关系
上一节我们已经介绍了 Memory。
很多同学容易把 Memory 与 RAG 混淆。
实际上,它们解决的是两类不同的问题。
可以用下面这张图理解:
Agent ┌─────────────────┐ │ LLM │ └────────┬────────┘ │ ┌──────────┴──────────┐ │ │ ▼ ▼ Memory RAG (历史记忆) (知识检索)两者最大的区别就是:
| Memory | RAG |
|---|---|
| 保存 Agent 自己经历的信息 | 查询外部知识 |
| 用户画像 | 企业知识库 |
| 历史任务 | PDF、Word |
| 用户偏好 | API 文档 |
| 长期存在 | 根据需要动态检索 |
举个例子:
用户问:
推荐一本机器学习的书。Agent 首先查询 Memory:
用户喜欢 Python。然后查询 RAG:
Hands-On Machine Learning最终生成:
考虑到你一直使用 Python, 推荐阅读: 《Hands-On Machine Learning》可以看到:
Memory 决定:
推荐给谁。
RAG 决定:
推荐什么。
二者结合之后,Agent 才真正具有个性化能力。
四、HelloAgents 中 RAG 的整体流程
HelloAgents 并不是简单调用一个向量数据库。
而是构建了一条完整的数据处理 Pipeline。
整体流程如下:
外部文档 │ ▼ MarkItDown 文档解析 │ ▼ Document Processor │ ▼ 文本切分(Chunk) │ ▼ Embedding 向量化 │ ▼ Qdrant 向量数据库 │ ──────────────────────────────── │ 用户提问 │ ▼ Query Embedding │ ▼ Vector Retrieval │ ▼ Top-K 文档返回 │ ▼ Prompt Augmentation │ ▼ LLM │ ▼ 最终回答整个流程可以分成两个阶段:
第一阶段:知识库构建(Offline)
这一阶段只需要执行一次。
主要完成:
- 读取文档;
- 文档解析;
- 文本切分;
- Embedding;
- 写入向量数据库。
完成之后:
知识库就建立好了。
以后无需重复执行。
第二阶段:在线检索(Online)
真正回答问题时:
用户提出问题:
Transformer 是什么?系统立即:
- 对问题进行 Embedding;
- 在 Qdrant 中搜索;
- 找到最相关几个 Chunk;
- 拼接 Prompt;
- 调用 LLM。
整个过程通常只需要几百毫秒。
相比直接让模型"猜",准确率会大幅提升。
下面继续第二部分。这一部分重点讲解HelloAgents 如何构建知识库,也是官方 8.3 节最重要的内容之一。
五、知识库是如何构建的?
上一节我们已经了解了 RAG 的整体流程。
那么,一个问题来了:
知识库中的数据是如何建立起来的?
例如,我们希望 Agent 能够回答下面的问题:
实验室规章制度有哪些? 这篇论文主要讲了什么? SpringBoot 项目如何部署? 公司的 API 如何调用?首先需要把这些文档导入到知识库。
HelloAgents 将整个知识库构建过程划分为几个步骤:
原始文档 │ ▼ MarkItDown 文档解析 │ ▼ Document Processor │ ▼ 文本切分(Chunk) │ ▼ Embedding 向量化 │ ▼ Qdrant 向量数据库只有完成整个流程之后,这些文档才能真正被 Agent 检索。
六、文档解析(MarkItDown)
现实中的知识通常不会直接以纯文本形式存在,而是保存在各种文件中,例如:
- Word(.docx)
- PowerPoint(.pptx)
- Markdown
- HTML
- TXT
如果直接读取这些文件,LLM 很难理解其中复杂的格式。
因此,HelloAgents 首先引入了MarkItDown。
什么是 MarkItDown?
MarkItDown 是微软开源的一个文档转换工具,它能够将多种格式的文件统一转换为Markdown。
例如:
PDF Word PowerPoint HTML经过 MarkItDown 后,都可以转换为:
# 第一章 这是正文…… ## 1.1 ……为什么选择 Markdown?
因为 Markdown:
- 保留了标题层级;
- 保留了列表结构;
- 保留了代码块;
- 文本更加规整。
相比直接提取纯文本,Markdown 更容易进行后续处理。
因此,HelloAgents 将 Markdown 作为知识库的统一中间格式。
七、为什么不能直接保存整个文档?
很多同学刚接触 RAG 时都会想到:
既然已经得到 Markdown,为什么不直接存入数据库?
原因主要有两个。
原因一:上下文窗口有限
例如:
一本教材:
300 页一篇论文:
40 页一个产品手册:
1000 页如果全部放进 Prompt:
Prompt + 整本书不仅速度非常慢,而且已经超过 LLM 的上下文长度。
因此:
必须切分。
原因二:检索精度下降
例如:
一本《机器学习》教材包含:
第一章 绪论 第二章 线性回归 第三章 决策树 第四章 神经网络 第五章 Transformer用户提问:
Transformer 是什么?真正需要的其实只有:
第五章。
如果整个文档作为一个整体进行检索,那么系统返回的将是整本教材。
不仅浪费 Token,还会干扰模型理解。
因此:
RAG 通常都会进行Chunk(文本切块)。
八、Chunk(文本切分)
Chunk 是整个 RAG 中最重要的概念之一。
它的本质就是:
把长文档切成许多较小的语义片段。
例如:
一本书:
机器学习教材切分之后:
Chunk1 机器学习概述 ------------------- Chunk2 线性回归 ------------------- Chunk3 决策树 ------------------- Chunk4 Transformer以后:
用户问:
Transformer系统直接返回:
Chunk4即可。
Chunk 多大比较合适?
Chunk 并不是越大越好。
也不是越小越好。
例如:
Chunk 太小:
Transformer 是 一种一句话就切断。
语义已经丢失。
Chunk 太大:
整个 PDF又失去了检索意义。
因此:
HelloAgents 建议按照:
- 固定长度
- 语义边界
- 标题结构
综合进行切分。
例如:
Markdown ↓ 一级标题 ↓ 二级标题 ↓ 段落尽量保证:
一个 Chunk 表达一个完整主题。
这样 Embedding 的效果最好。
九、Embedding:让计算机理解语义
完成 Chunk 之后,
接下来就是整个 RAG 最核心的一步:
Embedding(文本向量化)。
计算机并不能直接理解:
Transformer Python 深度学习这些文字。
因此:
需要转换成数学向量。
例如:
Transformer经过 Embedding:
[0.24, -0.18, 0.61, ……]而:
Attention Mechanism可能变成:
[0.21, -0.15, 0.59, ……]虽然两个句子不同。
但是:
它们向量距离很近。
说明:
语义相似。
Embedding 的作用
Embedding 最大的作用就是:
把自然语言映射到向量空间。
以后:
不是比较:
字符串是否相同而是比较:
两个向量是否接近例如:
数据库中保存:
Python用户输入:
机器学习编程语言关键词完全不同。
但是:
Embedding 后:
两个向量非常接近。
因此:
仍然能够成功检索。
这就是:
Semantic Search(语义检索)。
相比传统关键词搜索,它更加智能。
十、Qdrant:向量数据库
得到 Embedding 后,
所有 Chunk 都需要保存起来。
HelloAgents 选择使用:
Qdrant
作为默认向量数据库。
它的作用可以理解为:
SQLite 负责保存文本。 ↓ Qdrant 负责保存向量。例如:
Chunk: Transformer 于 2017 年提出。数据库中实际上保存的是:
文本 + Embedding + Metadata其中 Metadata 可以包括:
文件名称 章节 页码 标签 来源以后检索时:
不仅能够返回内容。
还可以告诉用户:
这段内容来自哪篇文档、哪一章节。
为什么使用向量数据库?
假设知识库有:
100 万个 Chunk如果每次都逐个计算相似度,
速度会非常慢。
因此:
Qdrant 内部采用了专门的近似最近邻(Approximate Nearest Neighbor,ANN)索引算法,例如 HNSW,使其能够在海量向量中快速找到最相似的几个结果。
整个过程可以表示为:
用户问题 │ ▼ Embedding │ ▼ Qdrant │ ▼ Top-K Chunk通常只需要几十毫秒即可完成一次检索。
十一、HelloAgents 中知识入库流程
综合前面的内容,HelloAgents 的知识库建立过程可以总结为:
PDF / Word / Markdown │ ▼ MarkItDown │ ▼ Markdown 文本 │ ▼ Document Processor │ ▼ Chunk 切分 │ ▼ Embedding │ ▼ Qdrant 存储至此,一份文档就真正变成了可检索的知识库。
以后用户每提出一个问题,系统都可以快速定位到最相关的几个 Chunk,再交由大语言模型进行回答。
下面是第三部分,也是整个HelloAgents 第8.3节 RAG博客的最后一部分。这一部分结合 HelloAgents 官方实现,介绍检索阶段、MQE、HyDE、RAGTool,最后进行总结。
十二、在线检索:RAG 如何回答用户问题?
完成知识库构建之后,真正的 RAG 才开始发挥作用。
与传统数据库查询不同,RAG 并不是根据关键词直接查找文本,而是经历一套完整的语义检索流程。
整个在线检索流程如下:
用户提问 │ ▼ Query Embedding(问题向量化) │ ▼ Vector Search(向量检索) │ ▼ 返回 Top-K Chunk │ ▼ Prompt Augmentation(构建上下文) │ ▼ LLM │ ▼ 最终回答整个流程通常只需要几百毫秒。
第一步:Query Embedding
首先,系统不会立即调用 LLM。
而是先对用户问题进行Embedding。
例如:
用户: SpringBoot 如何整合 Redis?Embedding 后:
[0.31, -0.24, 0.61, ......]这里使用的 Embedding 模型与建立知识库时保持一致。
这样,问题和知识库中的 Chunk 才位于同一个向量空间,可以直接计算相似度。
第二步:Vector Search
随后,系统进入向量数据库。
例如知识库中存在:
Chunk1 SpringBoot 整合 MySQL -------------------- Chunk2 SpringBoot 整合 Redis -------------------- Chunk3 Spring Security经过相似度计算:
返回:
Top1 SpringBoot 整合 Redis Top2 Redis 配置说明而不是返回:
Spring Security这就是语义检索最大的优势。
即使用户没有出现完全相同的关键词,只要表达的含义相近,也能够正确找到相关内容。
第三步:Top-K 检索
实际应用中,系统一般不会只返回一个 Chunk。
而是返回:
Top-K例如:
Top1 Redis 配置 Top2 Redis 注解 Top3 RedisTemplate 使用为什么要返回多个?
因为:
一篇完整答案往往分散在多个 Chunk 中。
多个 Chunk 能够共同补充上下文。
当然:
Top-K 也不是越大越好。
如果返回:
Top=100大量无关内容反而会影响模型理解。
因此:
实际项目通常设置:
Top-K = 3~10十三、Prompt Augmentation(提示增强)
得到检索结果之后,
HelloAgents 并不会直接返回。
而是把这些内容重新组织成 Prompt。
例如:
用户问题:
SpringBoot 如何连接 Redis?检索结果:
RedisTemplate 配置…… @EnableCaching…… application.yml……最终 Prompt:
下面是检索到的资料: ...... 请根据这些资料回答用户问题。 问题: SpringBoot 如何连接 Redis?这样:
LLM 就拥有了回答问题所需的知识。
因此:
最终生成内容:
既符合知识库,
又保持自然语言表达。
十四、为什么普通 RAG 还不够?
虽然普通 RAG 已经能够解决很多问题。
但是:
实际项目中仍然存在一些挑战。
例如:
用户输入:
GPU 怎么训练?这个问题非常模糊。
GPU:
可能指:
CUDA PyTorch TensorFlow 显卡驱动 多 GPU如果直接检索,
容易找到错误 Chunk。
因此:
HelloAgents 进一步介绍了两种高级检索策略。
十五、MQE(Multi-Query Expansion)
MQE 全称:
Multi-Query Expansion
中文一般翻译为:
多查询扩展。
核心思想非常简单:
不要只搜索一次。
例如:
用户输入:
SpringBoot 权限管理系统首先让 LLM 自动生成多个相似问题:
SpringBoot RBAC Spring Security 权限 JWT 权限认证 用户角色管理随后:
分别进行检索:
问题1 ↓ 检索 问题2 ↓ 检索 问题3 ↓ 检索最后:
把所有结果合并。
这样:
能够覆盖更多知识。
相比:
一次检索。
MQE:
召回率更高。
MQE 的优点
例如:
知识库只有:
JWT用户却输入:
Token普通 RAG:
可能找不到。
MQE:
自动扩展:
JWT Access Token Bearer Token因此:
成功检索。
十六、HyDE(Hypothetical Document Embeddings)
HyDE 是近年来比较流行的一种 RAG 优化策略。
全称:
Hypothetical Document Embeddings
它的思想更加有趣。
不是:
直接检索。
而是:
先让 LLM:
“假装已经知道答案。”
例如:
用户:
介绍 Transformer。LLM:
首先生成:
Transformer 是 Google 在 2017 年提出的…… 采用 Self-Attention……虽然:
这不是最终答案。
但是:
已经形成了一篇"假设文档"。
随后:
系统:
不是 Embedding:
用户问题而是 Embedding:
假设答案为什么这样效果更好?
因为:
知识库中的 Chunk:
一般都是:
完整段落。
而不是:
一句问题。
Embedding:
完整段落。
通常比:
一句问题。
更加接近真正文档。
因此:
检索质量更高。
HyDE 工作流程
整个流程如下:
用户问题 │ ▼ LLM 生成假设答案 │ ▼ Embedding │ ▼ Qdrant │ ▼ Top-K Chunk相比普通 RAG,
HyDE:
能够明显提高:
长文本检索效果。
十七、HelloAgents 中的 RAGTool
为了方便开发者使用,
HelloAgents 将整个 RAG 封装成:
RAGTool开发者无需关心:
Embedding、
数据库、
检索流程。
只需要调用接口即可。
添加文本
例如:
rag_tool.execute("add_text",text="Transformer 于 2017 年提出。")系统自动完成:
Chunk ↓ Embedding ↓ Qdrant添加文档
例如:
rag_tool.execute("add_document",path="paper.pdf")内部流程:
PDF ↓ MarkItDown ↓ Markdown ↓ Chunk ↓ Embedding ↓ Qdrant整个过程无需人工干预。
搜索知识
例如:
rag_tool.execute("search",query="Transformer")系统返回:
Top-K Chunk开发者可以:
自己处理。
也可以:
继续交给:
LLM。
直接问答
HelloAgents 还进一步封装:
rag_tool.execute("ask",query="Transformer 是什么?")系统自动完成:
Embedding ↓ Vector Search ↓ Prompt ↓ LLM ↓ 最终回答对于开发者来说,
几乎只需要:
一句代码。
即可拥有:
完整 RAG 能力。
十八、Memory + RAG:构建完整的 Agent 知识体系
至此,我们已经学习了第八章的两个核心模块:
- Memory(记忆)
- RAG(检索)
二者并不是竞争关系,而是现代 Agent 中最重要的两个组成部分。
可以通过下图理解:
Agent │ ┌────────────┴────────────┐ │ │ ▼ ▼ Memory RAG 保存历史经验 查询外部知识 │ │ └────────────┬────────────┘ ▼ LLM │ ▼ 最终回答其中:
Memory 负责:
- 用户画像;
- 长期偏好;
- 历史任务;
- 对话记录。
RAG 负责:
- 企业知识库;
- PDF;
- API 文档;
- 最新论文;
- 私有数据。
只有两者结合,Agent 才能够真正做到:
既记得过去,又知道现在。
十九、本章总结
通过 HelloAgents 第八章的学习,我们系统了解了 Agent 中两项关键能力:Memory(记忆)与RAG(检索增强生成)。
对于 RAG 部分,可以总结为以下几点:
RAG 的本质是“先检索,再生成”。它通过在生成答案前引入外部知识,有效弥补了大语言模型知识过时、无法访问私有数据以及容易产生幻觉等缺陷。
知识库构建(Indexing)是 RAG 的基础,包括文档解析(MarkItDown)、文本切分(Chunk)、Embedding 向量化以及 Qdrant 向量存储等步骤,这些操作通常离线完成。
在线检索(Retrieval)包括 Query Embedding、Vector Search、Top-K 检索和 Prompt Augmentation。系统首先检索最相关的知识片段,再将其作为上下文交给 LLM,从而生成更加准确、可靠的回答。
MQE(Multi-Query Expansion)和 HyDE(Hypothetical Document Embeddings)是提升检索质量的重要策略。前者通过扩展多个查询提高召回率,后者通过生成假设文档改善向量表示,二者都能够有效增强 RAG 的性能。
HelloAgents 提供了高度封装的
RAGTool,开发者只需调用简单接口即可完成文档导入、知识检索和问答,大大降低了构建 RAG 系统的开发难度。