聊到 RAG 的时候,我先问了一句:
查资料,给模型提供依据?
这句话我能听懂。接着又聊到知识库、向量数据库、工具调用、长期记忆,我就有点绕了。
查个资料,要这么多东西吗?它们的能力是不是有些重复?模型到底是自己去查,还是程序查好了直接传给它?
后来还是拿一次完整的对话来看,才容易理解:用户说了一句话,程序做了什么,模型收到了什么,然后又发生了什么。
1. 先说 RAG,它就是怎么查资料、怎么用资料
假设你在公司做一个 AI 助手。公司有自己的产品手册、操作文档,还有一些内部规定。
这些资料,模型未必知道。直接问它,可能会得到一个通用回答,但不一定适合你们公司的情况。
那就先找出相关资料,连着问题一起发给它,让它根据资料回答。这个过程就是 RAG,中文叫检索增强生成。
知识库就是这批可以拿来查的资料。它可以用一套文档系统来管理,也可以接在现有数据库或者搜索系统上,不一定要重新建库。官方说明
比如,公司有一万篇文档。程序从里面找到跟当前问题有关的五段,交给模型。这里面:
- 一万篇文档,是知识库里的资料。
- 找资料、用资料回答,是 RAG 的过程。
- 真正发给模型的那五段,才进入这次的上下文。
库里存了多少,和这次模型看到了多少,是两回事。
模型这次看过资料,通常不会因此修改自己的权重。下一次调用还要用这段内容,应用就得把它带上。
2. 那为什么还要向量数据库?普通数据库不能查吗?
我一开始也是这么想的:文档里搜一下关键词,不就找到了吗?
有些问题,确实这样就能找到。比如你问:
电脑报 E102,开不了机,怎么办?
拿 E102 去搜就很直接。错误码、产品型号、接口名,都很适合这样查。
这里借一个错误码举例,E102 不对应某款产品的真实故障。
但如果用户没说错误码,只说"开不了机",文档里写的却是"设备无法启动",仅靠相同字词去匹配,就可能漏掉。
向量检索主要解决这类问题:说法不一样,意思接近,也有机会找出来。
先用 Embedding 模型把文档片段变成一组数字,也就是向量。用户提问时,再用匹配的 Embedding 模型处理问题,比较向量,找出相似的内容。
向量数据库负责存和查这些向量。查到之后,再取出对应的原文,交给回答问题的模型。
Embedding 模型帮忙找资料,回答模型看原文、组织答案。通常不用把搜索用的那串向量直接交给回答模型。
不过,"意思接近"不等于"这段资料一定对"。可能都是启动问题,但设备型号不同,处理方法也不一样。找到之后,还是得看内容是否适用。
普通数据库也能通过扩展支持向量检索,搜索引擎也可以同时支持两种方式。选的时候,要看它具体能怎么查,不能只看名字。
两种一起查,就是这里说的混合检索
还是刚才那个问题,E102 用关键词查,"开不了机"用语义查。把两边的结果合并、去重,再排序,选出相关原文。

这样既能抓住具体错误码,也有机会补上说法不同的资料。不过,两路搜索的分数可能不能直接相加。可以用 RRF 这类方法按排名合并,也可以再做一次相关性排序。Elastic 的混合检索说明
混合检索说的是用哪些方式找资料。后面要聊的"谁决定去查",是另一个问题。
3. 文档那么多,难道每次都要读一遍?
如果有十万篇文档,每来一个问题,模型都要看一遍吗?
实际做的时候,会先整理资料,方便后续检索。文档新增或者更新时,处理对应的内容;用户提问时,再找相关片段。

先准备资料。 把 PDF、网页里的正文提取出来。长文档切成几段,保留标题、出处和版本,再按需要建立关键词索引、向量索引。后面文档改了,也要同步更新。
有人来问了,再查。 根据当前问题找候选片段,去重、筛选,必要时重新排序,再把选中的内容发给模型。能查哪些资料,还得看当前用户的访问权限。
比如库里有十万段内容,这次先取 20 个候选,最后留下 5 段,并限制参考资料总共不超过 4,000 token。这样传给模型的内容就有上限了。
这些数字只是举例,不能直接当成默认的最佳配置。
你真正动手时,会碰到这些参数:
| 参数 | 直白点说 |
|---|---|
| 切分长度、重叠长度 | 一段切多大,相邻两段重复保留多少内容 |
| Top-K | 先找出多少个候选 |
| 相似度阈值 | 相关性低到什么程度,就不要了 |
| 重排、最终条数 | 候选再排一下,最终留几段 |
| 资料 token 预算 | 这次最多给模型塞多少参考内容 |
框架可以提供这些配置,但填什么值,还得用自己的问题去试。尤其是相似度阈值,不同检索器的分数含义可能不一样,不能看到别人配了 0.8 就跟着填。Spring AI 的 RAG 配置说明
这里说的是不会每次把全部文档交给 LLM。底层搜索用了什么索引、扫描了多少数据,还得看检索系统的实现。
4. 我一直没弄清的,是谁在决定"去查资料"
前面说"模型查资料",我就有个疑问:
不是程序直接弄好了,再传给模型吗?
这两种做法都有,区别在于谁决定去查。
第一种,程序先查好,再交给模型
比如产品文档问答入口规定每次提问都先查知识库,程序收到问题,就可以直接开始检索。
程序把问题、查到的资料和回答要求整理好,再一起发给模型。

这里说模型"被动接收资料",是因为程序已经决定了要检索。模型收到资料后,仍然要判断内容、组织回答。
如果前面的检索和筛选没用聊天 LLM,那么这里可以只调用一次回答模型。
第二种,先让模型看问题,再决定要不要查
第一次调用时,程序先告诉模型:用户的问题是什么,你可以使用哪些工具,每个工具怎么用。
模型发现手里的信息不够,就返回一个工具请求。比如调用 search_docs,参数是 E102 启动失败。

这种带工具名和参数的调用方式,常被称为 Function Calling 或 Tool Calling。
返回工具请求的时候,搜索还没发生。 框架或程序收到请求,检查后执行工具,再把真实结果交给下一次模型调用。
模型看到结果以后,可能直接回答,也可能觉得还缺点东西,要求再查一次。工具请求和对应结果需要关联起来,接口里常见的字段是 tool_call_id。DeepSeek 的工具调用示例
"主动"说的是模型提出需求。读文件、查数据库、调接口,仍然由程序执行。
这两种方式也可以一起用:程序先查一批基础资料,模型觉得不够,再请求工具补查。
5. 查资料大概明白了,再看看"记忆"
聊到记忆时,我比较关心的是:什么时候进长期记忆,什么时候留在短期记忆?又是存在哪里?

刚才聊过什么,先跟着这次会话走
你已经说过设备型号,模型也查过两篇手册,还给过你一组排查步骤。这些都是当前会话里有用的信息。
这些信息保留下来后,你说"展开第二步",程序就能把对应的前文带给模型。这类服务于当前会话的信息,通常叫短期记忆。
它可以在程序内存里,也可以存在文件或者数据库里。要在重启后接着聊,就得有相应的持久化机制。
"短期"主要说的是使用范围。聊天记录即使存进数据库,也仍然可以是当前会话的短期记忆。
有些东西,下次聊天还用得上
比如你说:"以后回答我,先说结论。"
这句话就可能适合存成长期偏好。以后开了新会话,程序再取出来,交给模型参考。
什么时候写进去,要看应用怎么设计。有的在用户明确说"记住这个"时保存,有的在任务结束后整理,也有的会在后台提取已经确认的事实。
也不用什么都记。临时日志、模型猜测的原因,未必值得当成长期事实保存。已有记忆还得能改、能删,否则错误也会一直带着走。
长期记忆可以放文件,也可以放数据库。偏好就那么几条,按用户 ID 读出来就行;条目多了,需要按意思找相关内容,再考虑向量检索。
那它跟 RAG、上下文是什么关系?
RAG 可以去知识库找产品手册,也可以通过类似的检索过程,从记忆库里找出以前确认过的信息,再决定哪些放进这次上下文。
保存信息、找到信息、把信息交给模型,是三个动作。 存下来了,不代表每次都会全部发过去。
把一大段聊天压成摘要,也不会自动变成长期记忆。摘要可能只是为了让当前会话继续聊下去。记忆机制的官方说明
图里把几种来源放在了一起。实际调用时,哪个有用就取哪个,不要求每次全部用上。
6. 放到一次完整会话里,到底调用了几次模型?
假设我先问:
电脑报 E102,开不了机,怎么办?
模型回答之后,我又追问:
把排查步骤展开说。
用户只说了两句话,背后却可能已经调用了四次 LLM。

第一次,模型说需要查资料。 程序把问题、相关记忆和工具说明发过去,模型返回一个搜索请求。这时还没有搜索结果。
第二次,模型看到了搜索结果,又想看详情。 程序执行搜索,把请求和结果带给模型。假设返回的是几个标题和摘要,模型选择其中一篇,要求读全文或相关章节。
第三次,资料够了,给用户答案。 程序把读到的内容交回来,模型整理排查步骤,并带上出处。
第四次,回答用户的追问。 用户要求展开步骤,程序带上必要前文和新问题,再调用模型。这次已有资料够用,就可以直接回答。
用户说一次话,和调用一次 LLM,不能直接画等号。
四次只是这个例子。如果第一次搜索就返回了足够的资料,模型第二次调用就可能直接回答;问题复杂一些,也可能继续补查。一次模型响应里,还可能同时提出几个工具请求。
7. 那每次发给模型的东西,都是什么?
接下来我想看的是:第一次发给模型什么,第二次又多了什么?
图里用 C1、C2、C3、C4 表示这四次输入。

第一次带问题和必要说明;第二次加入工具请求和搜索结果;第三次再加入详情;用户追问时,把前面的回答和新问题带上。
这里假设内容还装得下,所以前文可以继续保留。内容变长以后,就需要筛选或者压缩了。
这些内容都会影响本次模型输出,可以先从这个角度理解"提示词"。但调用 API 时,通常会分成消息、工具定义、工具结果等结构,不一定全拼进一个 system 字段里。
里面大概有这些东西:
- 系统或开发者写的规则,比如回答要求、任务边界。
- 这次可用的工具,以及参数说明。
- 必要的历史对话、当前问题。
- 工具请求和对应结果,以及选中的资料、记忆。
资料原文也要和程序给模型的指令分清楚,不能因为文档里写了一句命令,就当成应用要求它执行的命令。
以 DeepSeek 的 /chat/completions 为例,它是无状态接口,后续请求需要的上下文由客户端提供。有些产品或 API 会代管会话,具体传法会不一样。多轮对话说明
不管由谁管理会话,都得确认一件事:这条信息虽然存着,但这次模型实际拿到了吗?
8. 一直加下去,上下文不会挤爆吗?
资料、历史、工具结果都往里面放,确实不能无限加。
对于输入和输出共用窗口的模型,可以先这么算:
text
输入内容 + 预留输出 + 安全余量 ≤ 上下文窗口
输入不能塞满,还得给模型生成回答留地方。最大输入、最大输出和推理 token 怎么算,要看具体接口;缓存命中的内容也仍然会占上下文容量。上下文窗口说明
应用里一般要做一些取舍:
- 结果太长,就只带需要的部分。 比如长日志、整本手册先放外面,需要哪段再读哪段。
- 记忆太多,就挑相关的。 这次问电脑故障,没必要把用户所有偏好、所有历史都带上。
- 对话太长,就整理前文。 保留近期消息和关键约束,较早内容可以做摘要,但也要接受摘要可能丢细节。
- 发送前检查长度。 同时保留工具请求与结果的对应关系,避免只剩一个孤立结果。
做摘要、提取记忆,也会调用模型吗?
如果用 LLM 来做,就得再算一笔。
普通查库、读文件、保存记录,通常由程序执行。组装消息、按固定规则裁剪,也通常不用聊天 LLM。
如果让 LLM 总结聊天历史、提取长期记忆、改写搜索词,就会增加模型调用。
比如前面已经调用了四次,又让 LLM 做了一次摘要、提取了一次记忆,那就是六次聊天 LLM 调用。生成向量用的 Embedding、专门做重排的模型,以及失败后的重试,还要按实际情况另外统计。
两次调用的消耗也不一定一样。一次只发了一句话,一次带了几千字资料,输入长度就不一样;回答写多长,也会影响用量。
所以看一个 Agent 的消耗,得同时看调用了几次、每次带了多少内容、最后生成了多少内容。
9. 所以,要把这些做出来,需要几个框架?
一个框架能接模型,也能管工具调用、RAG 和记忆,就可以先围绕它来做,再接上需要的模型和存储组件。别的框架也有这些能力,不代表这个应用里还得再放一个。
具体用 AgentScope、Spring AI,还是其他框架,得结合语言和版本,看它已经帮你做到哪一步。选完框架,资料怎么整理、哪些记忆要保存、上下文留多大,还是得自己定。
如果现在就动手,我会先把范围缩小:找一个产品,准备几份手册和一组问题。先看能不能搜到正确的段落,再接模型,让它根据段落回答。
这个过程跑通以后,再加会话历史,试试"第二步是什么""接着刚才说"这些追问。需要模型自己补查时,再加检索工具;确实需要跨会话记东西时,再加长期记忆。
每加一步,都检查发给模型的内容和真实返回结果,出了问题也容易定位。
到这里,我大概就能串起来了:资料存在外面,程序负责查找、筛选、组织,模型拿到上下文后回答或请求工具,工具执行完再把结果交回来。
以后碰到"它怎么没记住""为什么又查了一次""怎么用了这么多 token",就可以顺着这个过程往回查。比单独听一句"支持 RAG"或者"支持记忆",清楚多了。
文中用到的资料
下面是这次核对过的官方文档,核对日期为 2026-10-16。文章里的会话是为了讲清机制举的例子,没有拿这些框架做性能或费用实测。