【从零理解 Spring AI · 02】大模型为什么"记不住"也"不知道"

写在开头

上一篇结尾我留了一个悬念:模型的三个边界,决定了 AI 应用的三套机制。

这篇专门讲其中最容易被误解的两个:“记不住"和"不知道”。

先看两个我亲身踩过的场景:

场景一:我告诉模型"我叫张三,负责订单系统",聊得好好的。第三轮我问"我负责哪个系统?“——它猜了一个"支付系统”。

场景二:我问它"你们最近发布的模型有什么新特性?"——它滔滔不绝讲了一堆特性名,有条有理。然后我一对官方文档,一半是编的。

一开始我以为这是模型"不够聪明"或者"版本太旧"。后来才明白:这两个行为都不是 bug,而是这类系统的构造方式决定的。搞不清这两件事,后面的对话记忆、检索增强就永远只能"照抄配置",不知道自己在配什么。

这篇就把这两个边界掰开讲透。不用任何数学,只需要一个比喻和几个实验。


一、它根本没有"记忆"这个部件

1.1 关键认知:每次请求都是独立的

很多人对大模型的想象是这样的:

用户 ──→ [ 会思考、有记忆的大脑 ] ──→ 回答

觉得模型像一个大脑,聊过的东西会"存在里面"。

真实情况更接近这样:

用户 ──→ [ 一个无状态的函数 ] ──→ 回答
              ↑
         输入什么就处理什么

模型是一个纯函数:你发一段文字过去,它根据这段文字算出下一段文字返回。它内部没有一块"存储区"在等着装你的对话历史。 这次请求和上次请求之间,没有任何东西被保留下来。

所以"我叫张三"这句话,在第一轮请求结束后就消失了。第二轮你说"我叫什么",模型收到的输入里只有这一句——它怎么答?

1.2 用 Spring AI 把这件事做实

不废话,直接验证。写两个独立的调用:

@RestController
public class MemoryDemoController {

    private final ChatClient chatClient;

    public MemoryDemoController(ChatClient.Builder builder) {
        this.chatClient = builder.build();
    }

    @GetMapping("/round1")
    public String round1() {
        return chatClient.prompt()
                .user("记一下:我叫张三,负责订单系统。")
                .call()
                .content();
    }

    @GetMapping("/round2")
    public String round2() {
        return chatClient.prompt()
                .user("我叫什么名字?负责什么系统?")
                .call()
                .content();
    }
}

先访问 /round1,模型会回应"好的张三,已记下"之类的确认(注意:它只是礼貌性地复述,什么都没存)。

再访问 /round2,典型输出是:

抱歉,您没有告诉过我您的名字,我也不知道您负责什么系统。

两个请求之间唯一的联系,是你的浏览器会话——而不是模型。 这就是"无状态"三个字的具体样子。

1.3 那多轮对话是怎么实现的?

既然模型无状态,那 ChatGPT 那种"连续对话"是怎么回事?

答案朴素得让人失望:每一轮,把之前的对话历史全部重新发一遍。

第 1 轮发送:[ 你叫什么 ]
第 2 轮发送:[ 你叫什么, 我叫张三, 我叫什么名字? ]
第 3 轮发送:[ 你叫什么, 我叫张三, 我叫什么名字?, 张三, ... ]

所谓"记忆",不是模型记住了,而是你每次都把"该记住的内容"抄在考卷最上面。

这个认知一建立,很多事情立刻通了:

  • 为什么对话越长,响应越慢、费用越高——历史是随请求重复发送的;
  • 为什么换一个新会话,模型就"失忆"了——新会话没有带历史;
  • 为什么"记忆"需要一个专门的组件来管——手工拼历史消息既繁琐又容易出错。

二、上下文窗口:一张固定大小的桌子

2.1 "带历史"有一个硬性上限

历史不能无限带。模型一次能处理的输入长度是有上限的,这个上限就是"上下文窗口"。

你可以把一次请求想象成一张固定大小的桌子:

上下文窗口:一张固定大小的桌子

一次请求的全部内容都要摆上这张桌子

桌子上要同时放下四样东西:

内容说明会变化吗
系统提示词角色设定、行为规则基本固定
对话历史之前每一轮的问答每轮都在变长
本轮问题用户这次问的每轮不同
回答空间模型输出的预留位置—

注意最后一样:窗口装的不只是输入,还包括模型的输出。你让它写一篇长文,就得给它留出足够的空位。

2.2 桌子坐不下怎么办

历史一直变长,迟早把桌子占满。这时会发生两种事之一:

  • 框架帮你截断:把最早的历史丢掉,只留最近的——于是模型"忘了"开头说过什么;
  • 直接报错:请求被拒绝。

无论哪种,结果都是同一个:你无法依赖"很久之前说过的话"。

2.3 由此推出的记忆策略

理解了"桌子"模型,记忆的所有设计就只是在回答一个问题:桌子坐不下时,留什么、丢什么?

三种典型策略:

策略做法代价
全量携带历史全带上简单可靠,但很快撑爆窗口
滑动窗口只带最近 N 轮早期信息直接丢失
摘要压缩把旧历史压成一段摘要省空间,但细节没了

没有完美选项,都是取舍。Spring AI 把这些做成了可配置的能力,下一篇之后的记忆篇会展开。


三、它"不知道":知识有一个截止点

3.1 模型的世界是冻结的

第二个边界更容易被忽略。

模型的能力来自训练——读海量文本,学语言规律。而训练总要在一个时间点结束。这意味着:

模型知道的一切,都停留在训练数据截止的那一刻。

在那之后发生的事,它一无所知:

  • 你公司的产品文档、内部制度——训练数据里根本不存在;
  • 今天的新闻、昨天的价格——发生在截止点之后;
  • 只有你团队知道的那套黑话——从来没人公开写过。

它不是"不想说",是真的没见过。

3.2 比不知道更麻烦的:它不知道自己不知道

如果模型在不知道时老老实实说"我不知道",那问题不大。

坏就坏在它的生成机制上:模型是"接龙机器"——根据前文,一个词一个词地生成"最像接下来会出现的词"。它没有"这道题我不会"的开关,它的工作方式就是无论如何都要把话接下去。

于是当你问一个它不知道的问题时,会发生什么?它按照"这类问题通常怎么回答"的语言模式,流畅地生成一段看起来很专业的答案——里面的机构名、日期、参数、结论,全是语言惯性拼出来的。

这就是"幻觉"的来源:不是它在撒谎,而是"编"和"答"在它那里是同一个动作。

3.3 两个高危场景

结合实战,这两种提问方式最容易触发幻觉:

  1. 问具体事实:某个函数的参数名、某次发布的日期、某个数字——语言模式能编出以假乱真的样子;
  2. 问你的私有领域:内部系统、行业黑话——模型没见过,只能拿"最相似公开知识"来套。

应对思路也是两条,正好对应后面的两篇:

  • 教它说"不知道":在系统提示词里明确"资料中没有就说没有"——能压一部分,但压不干净;
  • 给它资料:把正确的、最新的资料检索出来放进上下文,让它照着资料答——这就是检索增强(RAG),第四辑展开。

四、两个边界,两条架构路线

现在把第一篇的预告兑现:为什么说这两个边界"直接决定了架构"?

模型的三个边界与对应机制

第一篇的边界-机制对照图,这里再放一次

把两篇的内容合起来看:

记不住(无状态 + 窗口有限)
   └─→ 每轮重带历史 → 需要管理历史 → 【对话记忆】组件

不知道(知识截止 + 必然幻觉)
   └─→ 需要外部资料 → 检索后放进上下文 → 【检索增强 RAG】

做不到(只会输出文字)
   └─→ 需要真实操作 → 让模型指挥代码 → 【工具调用】

Spring AI 里每一个"听起来很高大上"的组件,都是这三条推导链的终点。 记忆组件在管理"桌上放多少历史",RAG 组件在管理"桌上放哪些资料",工具调用在解决"模型说的话怎么变成真实操作"。

理解了边界,组件就不是 API,而是答案。


五、这篇的四个要点

  1. 模型是无状态函数——“记忆"的本质是每轮重发历史,没有任何东西存在模型"里面”。
  2. 上下文窗口是固定大小的——系统提示词、历史、问题、回答空间都要摆上去;历史越长,可用空间越少。
  3. 知识有截止点,且它不知道自己不知道——幻觉不是撒谎,是"编"和"答"共用同一个生成动作。
  4. 三个边界分别推导出三套机制——记忆、检索增强、工具调用,这就是 Spring AI 核心组件的"存在理由"。

延伸思考

生产环境里,"记忆怎么管"会立刻遇到一个现实问题:多用户共用同一个模型服务,历史怎么隔离? 张三和李四的对话历史混在一起是要出事故的。Spring AI 用"会话 ID"来隔离,而会话 ID 怎么设计、历史存哪里(内存?数据库?Redis?)、存多久——这些是记忆篇要展开的工程问题。

先记住这个方向即可。


下一篇

边界讲完了,下一篇开始正式进入 Spring AI 的世界——

《Spring AI 在 Spring 生态里的位置》:为什么"自己发 HTTP 调模型"是下策?Spring AI 的四层结构分别管什么?以及它和你已经用了多年的 Spring Boot 自动配置是怎么配合的。

如果你已经忍不住想先写代码,也可以先跳去看那篇的最小可用示例——但这篇的边界认知,是你后面读懂每一段设计的前提。


本专栏按 Spring AI 当前版本讲解。

Logo

更多推荐