今天的 Agent 学习总结:从 RAG 到 ReAct,理解智能体的记忆与工具调用

一、今天学习的整体方向

前几天我主要学习了 Agent 开发中的一些基础概念,比如:

Prompt
Tool
Function Calling
MCP
Skill
RAG
Embedding
Vector Store

这些内容让我对 Agent 有了一个初步认识:
Agent 并不是简单地调用大模型接口,而是要让大模型具备一定的“任务理解能力”和“工具使用能力”。

今天的学习是在前几天的基础上继续递进,重点放在了几个更具体的问题上:

1. RAG 在 Agent 中到底起什么作用?
2. ReAct 模式为什么适合做对话型 Agent?
3. Agent 是如何选择和调用工具的?
4. 多轮对话记忆是怎么实现的?
5. 最近窗口加载和 Token 限制有什么关系?

今天最大的收获是:

Agent 的核心不只是“回答问题”,而是能够结合上下文、调用工具、检索知识,并根据任务一步步完成目标。


二、重新理解 RAG:给 Agent 补充外部知识

前几天学习 RAG 时,我知道它的基本作用是:
让大模型能够结合外部资料回答问题。

今天进一步理解到,RAG 在 Agent 系统中更像是一个知识检索工具

大模型本身的知识来自训练数据,但训练数据存在几个问题:

1. 可能不是最新的
2. 不包含企业内部资料
3. 不包含用户上传的文档
4. 不知道具体项目中的业务规则

所以如果我们希望 Agent 回答某些私有知识、业务文档、接口说明、运维手册等内容,就需要通过 RAG 给它补充上下文。

RAG 的基本流程可以理解为:

文档上传
   ↓
文档解析
   ↓
文本切分
   ↓
生成 Embedding 向量
   ↓
存入向量数据库
   ↓
用户提问
   ↓
问题向量化
   ↓
相似度检索
   ↓
召回相关文本片段
   ↓
将文本片段和问题一起交给大模型
   ↓
生成最终回答

所以 RAG 并不是让大模型重新训练,也不是直接修改大模型参数,而是在提问时临时给大模型补充相关资料。

可以这样理解:

大模型负责理解和生成
RAG 负责查找外部知识
Embedding 负责语义向量化
向量数据库负责相似度检索

三、Chunk Size 和 Overlap 的理解

今天还继续理解了 RAG 中的文本切分问题。

文档通常不会直接整篇交给大模型,因为文档可能很长,超过上下文窗口限制。
所以需要把文档拆成一个个小块,也就是 Chunk。

1. Chunk Size 是什么?

chunk size 指的是每个文本块的大小。

例如一篇文档有 5000 个 token,我们可以按照一定大小切成多个 chunk:

chunk 1:第 1 - 500 token
chunk 2:第 501 - 1000 token
chunk 3:第 1001 - 1500 token

这样做的好处是:

1. 便于向量化
2. 便于相似度检索
3. 避免一次性塞入过长文本
4. 提高检索的精确度

2. Overlap 是什么?

overlap 指的是相邻两个 chunk 之间保留一部分重复内容。

比如:

chunk 1:第 1 - 500 token
chunk 2:第 451 - 950 token
chunk 3:第 901 - 1400 token

这里 chunk 1 和 chunk 2 之间就有 50 个 token 的重叠。

为什么要这样做?

因为如果完全硬切,可能会把一个完整语义拆开。

例如原文是:

Prometheus 可以采集服务的 CPU、内存、QPS 等指标,
这些指标可以用于判断服务是否存在异常。

如果刚好从中间切开,可能变成:

chunk 1:Prometheus 可以采集服务的 CPU、内存、QPS 等指标
chunk 2:这些指标可以用于判断服务是否存在异常

第二个 chunk 中的“这些指标”指代不清楚。
加入 overlap 之后,第二个 chunk 会带上一部分前文内容,让语义更加完整。

所以 overlap 的作用是:

防止语义被切断
保留上下文衔接
提高检索结果质量
让大模型获得更完整的背景

四、RAG 不是每次都必须使用

今天还理解了一个很重要的问题:

引入 RAG 之后,Agent 是不是每次回答都必须先查知识库?

答案是不一定。

如果用户问的是通用知识,比如:

Java 中 HashMap 的底层原理是什么?
什么是 HTTP?
什么是线程池?

这些内容大模型本身通常就可以回答,不一定需要查知识库。

但是如果用户问的是和外部资料、私有文档、上传文件相关的问题,例如:

这个文档里是怎么描述部署流程的?
系统异常码 1001 表示什么?
某个接口的业务规则是什么?

这时候就需要使用 RAG。

所以更合理的理解是:

通用问题:大模型直接回答
私有知识问题:通过 RAG 检索后回答
实时数据问题:通过工具查询后回答

这也说明 Agent 不应该只是固定地走一条流程,而应该根据用户问题判断是否需要调用外部能力。


五、从 Function Calling 到 Tool:让 Agent 具备行动能力

前几天学习 Function Calling 时,我理解到大模型可以返回一个结构化调用请求,例如:

{
  "name": "getWeather",
  "arguments": {
    "city": "北京"
  }
}

今天继续理解后发现,Function Calling 和 Tool Calling 本质上是在解决同一个问题:

如何让大模型从“只会生成文字”,变成“可以调用外部能力”。

普通的大模型只能回答:

我不知道当前天气。

但是接入工具后,它可以先调用天气工具,然后根据工具结果回答:


北京今天多云,气温约为……

在 Agent 中,工具可以有很多种:

知识库检索工具
天气查询工具
时间查询工具
数据库查询工具
日志查询工具
监控指标查询工具
文件解析工具

这些工具本质上可能只是后端中的普通方法,但当它们注册给大模型后,大模型就可以根据用户意图选择是否调用。

所以 Tool 的意义不是“多写几个方法”,而是给 Agent 增加外部行动能力。

可以这样理解:

Prompt 让模型知道要做什么
Tool 让模型知道可以怎么做
Function Calling 让模型能够表达要调用哪个工具
后端程序负责真正执行工具

六、ReAct:让 Agent 具备“思考 + 行动”的过程

今天学习中最关键的一个概念是 ReAct。

ReAct 可以拆成两个部分:

Reasoning + Acting

也就是:

推理 + 行动

普通问答模型的流程是:

用户提问
   ↓
大模型回答

而 ReAct Agent 的流程更像是:

用户提问
   ↓
模型分析问题
   ↓
判断是否需要工具
   ↓
选择合适工具
   ↓
调用工具
   ↓
观察工具返回结果
   ↓
继续推理
   ↓
生成最终回答

这就是 Agent 比普通大模型应用更进一步的地方。

例如用户问:

帮我分析一下为什么服务响应变慢。

普通大模型可能只能给出一些通用建议:

可能是 CPU 高、数据库慢、网络延迟、接口阻塞等。

但 ReAct Agent 可以进一步行动:

1. 先查询监控指标
2. 再查询最近日志
3. 根据错误信息判断可能原因
4. 最后生成排查建议

所以 ReAct 的价值在于,它让模型不仅能“想”,还能“做”。


七、多轮对话记忆:让 Agent 不只是单轮问答

今天还学习了 Agent 的多轮对话记忆。

如果没有记忆,模型每次只能看到当前这一句话。
例如:

用户:我正在学习 RAG。
AI:RAG 是检索增强生成……

用户:那它和 Embedding 有什么关系?

如果没有历史上下文,模型可能不知道“它”指的是 RAG。

所以 Agent 需要保存一定的对话历史,让模型理解上下文。

最简单的实现方式可以是:

sessionId
   ↓
保存当前会话的历史消息
   ↓
每次请求时加载最近几轮对话
   ↓
和用户新问题一起发送给大模型

这里的 sessionId 很重要,它用来区分不同会话。

例如:

sessionId = A:讨论 RAG
sessionId = B:讨论 Tool Calling
sessionId = C:讨论 Docker

如果没有 sessionId,不同会话的上下文可能混在一起,导致模型回答混乱。

所以 sessionId 的作用可以理解为:

区分会话
隔离上下文
支持多轮对话
避免历史串扰

八、最近窗口加载:解决上下文过长问题

今天还理解了“最近窗口加载”。

既然要支持多轮对话,那是不是应该把所有历史记录都传给大模型?

答案是否定的。

因为大模型有上下文窗口限制。
历史消息越多,占用的 token 就越多。

如果把所有历史都放进去,可能会带来几个问题:

1. Token 消耗变大
2. 请求成本上升
3. 响应速度变慢
4. 可能超过模型上下文限制
5. 太久远的历史还可能干扰当前回答

所以通常会采用最近窗口加载。

比如只保留最近 6 轮对话:

第 1 轮:丢弃
第 2 轮:丢弃
第 3 轮:保留
第 4 轮:保留
第 5 轮:保留
第 6 轮:保留
第 7 轮:保留
第 8 轮:保留

这样既可以保留最近上下文,又不会让历史无限增长。

最近窗口加载的作用是:

控制上下文长度
降低 token 消耗
保留近期语义
减少无关历史干扰
防止上下文超限

如果后续系统变得更复杂,还可以加入历史摘要压缩。

例如把很早之前的对话总结成一段摘要:

用户之前主要学习了 RAG、Embedding、向量数据库和 Tool Calling。

这样既保留长期记忆,又不会占用太多 token。


九、ChatMemory 的简单理解

今天还接触到 ChatMemory 的概念。

ChatMemory 可以理解为:

用于保存和管理对话历史的组件

在简单 Demo 中,可以先使用内存保存:

Map<sessionId, List<Message>>

这种方式简单直观,但有一个问题:

服务重启后,历史记录就没了。

所以它更适合学习 Demo 或临时会话。

如果要做得更完整,可以使用 Redis 或数据库保存历史。

例如:

Redis:适合保存短期会话,读写快,可以设置过期时间
数据库:适合保存长期历史,便于查询和审计

所以 ChatMemory 不完全等于缓存。
它的核心目的不是为了“加快查询”,而是为了让 Agent 能够理解多轮对话上下文。

可以这样区分:

缓存:主要为了提高访问速度
ChatMemory:主要为了保存对话上下文

当然,在工程实现中,也可以用 Redis 来实现 ChatMemory。


十、今天对 Agent 的理解进一步加深了

经过今天的学习,我对 Agent 的理解又清晰了一层。

最开始,我以为 Agent 可能就是:

大模型 + Prompt

后来学习 Tool 和 Function Calling 后,理解到 Agent 还需要:

大模型 + Prompt + 工具调用

学习 RAG 后,又发现 Agent 还需要:

大模型 + Prompt + 工具调用 + 外部知识检索

今天理解 ReAct 和 ChatMemory 后,认识更加完整:

Agent = 大模型 + Prompt + Tool + RAG + Memory + Reasoning

也就是说,Agent 不是一个单独的技术点,而是一套组合系统。

它至少包含几种能力:

理解用户目标
维护上下文
判断是否需要外部知识
选择合适工具
调用工具获取结果
根据结果继续推理
生成最终回答

这才是一个智能体逐渐成型的过程。


十一、今天的学习总结

今天主要学习和理解了 Agent 中几个比较核心的内容:

1. RAG 是 Agent 获取外部知识的重要方式
2. Chunk Size 控制文本块大小
3. Overlap 用来避免语义被切断
4. Tool Calling 让大模型具备调用外部能力的方式
5. ReAct 让 Agent 拥有“推理 + 行动”的流程
6. sessionId 可以实现多轮会话隔离
7. ChatMemory 可以保存上下文
8. 最近窗口加载可以降低 token 消耗
9. Redis 可以作为 ChatMemory 的一种实现方式

更多推荐