先说个事:你跟 ChatGPT 聊天,它从来不记得你前面说过什么。服务器那边啥也没存。每次都是你的手机或电脑,把你们整个聊天记录重新发过去,模型看完给你回一句,然后服务器就把这事忘了------干干净净,跟第一次见你一样。
这就是大模型服务的"无状态"本质。
搞明白这个,你就能看懂现在所有的 AI 应用------不管是聊天机器人,还是那些看起来很厉害的智能体,底层全是同一套东西在跑。
你发的其实是全部聊天记录
直接看代码。这是我写的一个最简单的例子,用 DeepSeek 的接口来模拟一轮对话:
javascript
js
import OpenAI from 'openai'
const client = new OpenAI({
apiKey: process.env.DEEPSEEK_API_KEY,
baseURL: 'https://api.deepseek.com'
})
// 我们自己记着聊天记录
const chatHistory = [
{ role: 'system', content: '你是一个严谨的助手' }
]
async function chat() {
// 第一轮:告诉模型我叫什么
chatHistory.push({ role: 'user', content: '请记住,我的名字是001' })
const res1 = await client.chat.completions.create({
model: 'deepseek-v4-flash',
messages: chatHistory // ← 把整个历史发过去
})
// 把模型的回复也记下来
chatHistory.push({ role: 'assistant', content: res1.choices[0].message.content })
// 第二轮:问它我叫什么
chatHistory.push({ role: 'user', content: '请问我的名字是什么' })
const res2 = await client.chat.completions.create({
model: 'deepseek-v4-flash',
messages: chatHistory // ← 又把整个历史发过去
})
chatHistory.push({ role: 'assistant', content: res2.choices[0].message.content })
console.log('第一轮回复:', res1.choices[0].message.content)
console.log('第二轮回复:', res2.choices[0].message.content)
console.log('聊天记录条数:', chatHistory.length)
// 输出: 5 条 (系统提示 + 我的问题 + 模型回复 + 我的问题 + 模型回复)
}
chat()
第二轮发请求的时候,messages 数组里已经有 4 条消息了------系统提示、第一轮的问题、第一轮的回复、第二轮的问题。服务器拿到这一大堆,才能"推理"出我叫 001。
服务器那边看到的是这样:
text
js
请求1 来了: ["你是严谨助手", "我叫001"]
→ 算完,回复,清空,忘了你
请求2 来了: ["你是严谨助手", "我叫001", "好的记住了", "我叫什么?"]
→ 算完,回复,清空,忘了你
两次请求中间,服务器什么都没留。模型好像"记得",只是因为我们的程序把历史消息又发了一遍。
这是故意这么设计的
为什么一定要这么设计?
想想看,要是每次聊天服务器都得替你记着------你是谁、聊到哪了、之前都说了啥------那一个人就得占一块地方。一万个人同时聊,就是一万份记录。这还是往少了算的,有的人可能聊了半小时,聊天记录攒了几十轮。
更要命的是多台机器一起工作的时候。
假如你有 10 台服务器轮流处理请求。要是服务器得记状态,那你的请求就必须每次发到同一台机器上,因为只有那台存着你之前聊的东西。这就叫"粘住",是搞服务器的人最头疼的事------万一那台机器坏了,上面所有人的聊天记录全没了。
不记状态就没这麻烦:
text
markdown
请求来了 → 随便哪台机器 → 处理 → 忘掉
→ 下次换一台 → 一样处理 → 忘掉
每台机器处理每个请求的逻辑完全一样。加机器、减机器、换机器、更新系统------全变成普通操作。这就是网页请求原本的设计思路,大模型的接口也照着这么做了。
聊天记录的问题:钱在偷偷变多
这个设计有个很现实的麻烦:你聊得越久,每次发消息花的钱越多。
因为每次发请求,聊天记录里的每个字都会被算钱。聊到第 20 轮的时候,你说的每一句新话,都在替前面 19 轮的历史付钱。
简单算一下:
text
第 1 轮: 花几十个字的钱
第 10 轮: 花两千个字的钱(历史堆起来了)
第 50 轮: 花一万五千个字的钱
第 200 轮: 花八万个字的钱
聊了两百轮,你最后一轮问的可能就二十个字,但你实际发出去的快八万个字。每一次发消息,都在替前面所有的历史买单。
而且各家模型价格差很多------便宜的一百万个字不到一块钱,贵的能到几十块。要是你的产品有一千个用户,一天几万次请求,这开销可不小。
还有一个不太容易注意的问题:模型注意力散掉了。
大模型一次能看的字数虽然越来越多,但它对长文字的处理不均匀。最前面和最后面的内容最受关注,中间的容易变模糊。你把几十轮之前的对话留在里面,它占着字数,但对模型帮你回答新问题,可能已经没用了。
于是有了三个阶段的改进
解决这些问题不是一步到位的。整个 AI 应用的开发,大概走了三步:
第一步:琢磨怎么说(提示词)
这步大家最熟。设计系统提示、给几个例子、调整措辞。本质上就是在"调参数"------通过改输入来影响输出。
但这儿有个很多人不说破的事:琢磨提示词很像抽奖。 你认真写的提示确实能提高好结果的概率,但不能保证。同样的提示跑十次,可能三次特别好、五次还行、两次完全不行。它让"中奖率"变高了,但离"能控制"还很远。
第二步:喂什么信息(上下文)
这步往前迈了一大块。思路变了:与其使劲打磨提示让模型"猜",不如直接把相关信息塞给它。
检索增强生成是最典型的做法------从知识库里搜出相关文件,拼进上下文,模型直接照着回答。各种工具和数据的接入,本质上也是同一件事:用标准的方式把东西塞进模型能看到的范围里。
核心道理:模型能做成什么样,由你给它的信息决定。 你给准的信息,它给准的答案。你让它瞎猜,它就瞎猜。
这步解决的是聊天记录"不够用"的问题------把真正有用的东西放进去,比把历史堆得老长有用得多。
第三步:让 AI 一直跑(循环)
这步是现在最前沿的。把模型从一个问一句答一句的工具,变成了能自己在循环里持续跑的"智能体"。
比如让模型自己读文件、写代码、跑测试、看报错、改代码、再跑,来回循环直到通过。
这步要解决的问题跟聊天记录直接挂钩:在长时间跑的循环里,管好上下文从"顺便弄弄"变成了"要命的事"。 一个智能体跑五十轮循环,如果不压缩和剪裁,花的钱会涨得很快,而且模型的注意力会彻底散掉。
回到开头
理解"不记事",是搞懂大模型应用的起点。
你写的每一个 AI 应用------不管多复杂、套了多少层框架------最底层都是同一件事:把一堆文字通过网络发给一个不记事的服务,拿回回复,把回复加到消息列表里,下次再一起发过去。
大模型没有记忆。"记忆"这件事,从头到尾是我们自己的程序在管。
搞明白这个,那些看起来花里胡哨的 AI 框架和架构,拆开看就都不神秘了。