同学们好哦
在 AI 应用开发(特别是大模型应用) 中,经常会提到两个概念:
- 短期记忆(Short-term Memory)
- 长期记忆(Long-term Memory)
可以把它理解为:
大模型像一个人聊天时的记忆系统
- 短期记忆 → 当前对话临时记住的东西
- 长期记忆 → 可以长期保存、随时取出来的东西
下面我们展开聊聊。
短期记忆(Short-term Memory)
短期记忆 = 当前对话上下文
本质就是:
Context Window(上下文窗口)
Context Window 也就是:
历史对话 + 当前输入 + 模型输出
例如:
用户:我叫小王
AI:好的小王
用户:我刚刚说我叫什么?
AI:小王
AI 能回答,是因为:
用户:我叫小王
AI:好的小王
用户:我刚刚说我叫什么?
这些 全部被放进 Context Window。
短期记忆的特点
| 特点 | 说明 |
|---|---|
| 临时 | 只在当前对话有效 |
| 有长度限制 | Context Window 限制 |
| 超出会被截断 | 旧消息会被删除 |
| 不会长期保存 | 新对话就没了 |
例如:
GPT-4o
sql
Context Window ≈ 128k tokens
如果对话太长:
最早的聊天记录会被删掉
所以 AI 会 "失忆" 。
Context Window 超长处理
有小伙伴问:历史会话越来越多,context window超长之后需要截断,这个截断的过程是谁处理的,工程还是模型。
答案:工程这边处理
一般来说,context window 发送给模型之前,都会把最后需要发送的内容在工程内计算一下,然后结合目标模型最大可接受context window的大小来判断 ,需要裁剪掉多少内容。否则有些模型在context window超出的时候会直接报错。
我们以gpt-4o 为例子,它的context window 大小限制是128k ,分别以 javascript/python/java三个语言的真实代码看一下。
js案例
js
import OpenAI from "openai"
import { encoding_for_model } from "tiktoken"
const client = new OpenAI({
apiKey: process.env.OPENAI_API_KEY
})
// GPT-4o 的 context window
const MAX_CONTEXT = 128000
// 给输出预留 token
const MAX_OUTPUT = 4000
// tokenizer
const encoder = encoding_for_model("gpt-4o")
// 计算 token
function countTokens(messages) {
let total = 0
for (const msg of messages) {
total += encoder.encode(msg.content).length
}
return total
}
// 裁剪历史
function trimMessages(messages) {
while (countTokens(messages) > MAX_CONTEXT - MAX_OUTPUT) {
// 删除最早的一条用户/assistant消息
messages.splice(1, 1)
}
return messages
}
async function chat() {
let messages = [
{ role: "system", content: "你是一个 helpful assistant" },
{ role: "user", content: "你好" },
{ role: "assistant", content: "你好!有什么可以帮你?" },
{ role: "user", content: "给我讲一下 JavaScript 的事件循环" }
]
// 检测并裁剪 context
messages = trimMessages(messages)
const response = await client.chat.completions.create({
model: "gpt-4o",
messages,
max_tokens: MAX_OUTPUT
})
console.log(response.choices[0].message.content)
}
chat()
python案例
python
from openai import OpenAI
import tiktoken
# 初始化客户端
client = OpenAI(api_key="YOUR_API_KEY_HERE")
# GPT-4o context window
MAX_CONTEXT = 128_000
MAX_OUTPUT = 4_000 # 给输出预留
# tokenizer
encoder = tiktoken.encoding_for_model("gpt-4o")
# 计算 token 数量
def count_tokens(messages):
total = 0
for msg in messages:
total += len(encoder.encode(msg["content"]))
return total
# 裁剪历史
def trim_messages(messages):
while count_tokens(messages) > MAX_CONTEXT - MAX_OUTPUT:
# 删除最早的用户/assistant消息(保留 system prompt)
messages.pop(1)
return messages
# 主聊天函数
def chat():
messages = [
{"role": "system", "content": "你是一个 helpful assistant"},
{"role": "user", "content": "你好"},
{"role": "assistant", "content": "你好!有什么可以帮你?"},
{"role": "user", "content": "给我讲一下 JavaScript 的事件循环"}
]
# 检测并裁剪 context
messages = trim_messages(messages)
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
max_tokens=MAX_OUTPUT
)
print(response.choices[0].message["content"])
if __name__ == "__main__":
chat()
java案例
java
<!-- Maven 依赖示例 -->
<dependencies>
<!-- OpenAI 官方 Java SDK -->
<dependency>
<groupId>com.openai</groupId>
<artifactId>openai</artifactId>
<version>1.1.0</version>
</dependency>
<!-- tiktoken-java 用于 token 计算 -->
<dependency>
<groupId>com.github.acheong08</groupId>
<artifactId>tiktoken-java</artifactId>
<version>1.0.5</version>
</dependency>
</dependencies>
java
from openai import OpenAI
import tiktoken
# 初始化客户端
client = OpenAI(api_key="YOUR_API_KEY_HERE")
# GPT-4o context window
MAX_CONTEXT = 128_000
MAX_OUTPUT = 4_000 # 给输出预留
# tokenizer
encoder = tiktoken.encoding_for_model("gpt-4o")
# 计算 token 数量
def count_tokens(messages):
total = 0
for msg in messages:
total += len(encoder.encode(msg["content"]))
return total
# 裁剪历史
def trim_messages(messages):
while count_tokens(messages) > MAX_CONTEXT - MAX_OUTPUT:
# 删除最早的用户/assistant消息(保留 system prompt)
messages.pop(1)
return messages
# 主聊天函数
def chat():
messages = [
{"role": "system", "content": "你是一个 helpful assistant"},
{"role": "user", "content": "你好"},
{"role": "assistant", "content": "你好!有什么可以帮你?"},
{"role": "user", "content": "给我讲一下 JavaScript 的事件循环"}
]
# 检测并裁剪 context
messages = trim_messages(messages)
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
max_tokens=MAX_OUTPUT
)
print(response.choices[0].message["content"])
if __name__ == "__main__":
chat()
长期记忆(Long-term Memory)
长期记忆 === AI 可以长期存储的数据
例如:
用户偏好
历史资料
知识库
用户画像
这些数据通常会存到:
数据库
向量数据库
知识库
当用户提问时:
用户提问
系统检索相关记忆
把记忆塞进 Prompt
再给模型
流程:
用户问题
↓
检索长期记忆
↓
拼进 Prompt
↓
发送给大模型
↓
生成回答
这其实就是:
RAG(Retrieval Augmented Generation)
举个完整例子
-
用户第一次和 AI 聊天时,告诉 AI 自己的偏好:
- "我喜欢美式咖啡"
- "我在学 JavaScript"
-
系统把这些信息存入 长期记忆(向量数据库)
-
下次用户提问时(此时可能已经切换了session):
- 系统先 检索相关长期记忆 ,这一步属于向量搜索的范围(Embedding),点击查看详细解析
- 把相关内容加入 Context Window
- 发送给模型
还是以js/python/java为代码实例,大家参考一下
js
js
import OpenAI from "openai";
import { encoding_for_model } from "tiktoken";
import { PineconeClient } from "@pinecone-database/pinecone";
// 初始化 GPT 客户端
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
// 初始化向量数据库(长期记忆)
const pinecone = new PineconeClient();
await pinecone.init({ apiKey: process.env.PINECONE_API_KEY, environment: "us-west1-gcp" });
const index = pinecone.Index("user-memory");
// GPT-4o context window
const MAX_CONTEXT = 128000;
const MAX_OUTPUT = 4000;
const encoder = encoding_for_model("gpt-4o");
// 计算 token 数量
function countTokens(messages) {
let total = 0;
for (const msg of messages) {
total += encoder.encode(msg.content).length;
}
return total;
}
// 裁剪短期记忆
function trimMessages(messages) {
while (countTokens(messages) > MAX_CONTEXT - MAX_OUTPUT) {
messages.splice(1, 1); // 删除最旧消息
}
return messages;
}
// 把长期记忆存入向量数据库
async function storeLongTermMemory(userId, text) {
// 1. 获取 embedding
const embeddingResponse = await client.embeddings.create({
model: "text-embedding-3-large",
input: text
});
const vector = embeddingResponse.data[0].embedding;
// 2. 存入向量数据库
await index.upsert({
upsertRequest: {
vectors: [
{
id: `${userId}-${Date.now()}`,
values: vector,
metadata: { text }
}
]
}
});
}
// 查询长期记忆(RAG)
async function retrieveLongTermMemory(userId, query, topK = 5) {
const embeddingResponse = await client.embeddings.create({
model: "text-embedding-3-large",
input: query
});
const vector = embeddingResponse.data[0].embedding;
const searchResponse = await index.query({
queryRequest: {
vector,
topK,
includeMetadata: true,
filter: { userId }
}
});
return searchResponse.matches.map(m => m.metadata.text);
}
// 主对话流程
async function chat(userId, userMessage) {
// 1. 检索相关长期记忆
const longTermMemory = await retrieveLongTermMemory(userId, userMessage);
// 2. 构建短期记忆
let messages = [
{ role: "system", content: "你是一个 helpful assistant" }
];
// 把长期记忆加入短期记忆
if (longTermMemory.length > 0) {
messages.push({
role: "system",
content: `用户的长期记忆信息:\n${longTermMemory.join("\n")}`
});
}
// 当前用户消息
messages.push({ role: "user", content: userMessage });
// 3. 裁剪 context window
messages = trimMessages(messages);
// 4. 生成回答
const response = await client.chat.completions.create({
model: "gpt-4o",
messages,
max_tokens: MAX_OUTPUT
});
const answer = response.choices[0].message.content;
console.log("AI:", answer);
// 5. 存储新的长期记忆(可选)
await storeLongTermMemory(userId, userMessage);
}
// --- 使用示例 ---
const userId = "user123";
// 第一次对话,存长期记忆
await storeLongTermMemory(userId, "我喜欢美式咖啡");
await storeLongTermMemory(userId, "我正在学习 JavaScript");
// 用户发问
await chat(userId, "给我推荐一款咖啡?");
await chat(userId, "帮我讲一下 JavaScript 的事件循环");
python
python
# pip install openai tiktoken pinecone-client
from openai import OpenAI
import tiktoken
import pinecone
import os
# 初始化 OpenAI
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
# 初始化 Pinecone
pinecone.init(api_key=os.getenv("PINECONE_API_KEY"), environment="us-west1-gcp")
index = pinecone.Index("user-memory")
# GPT-4o context window
MAX_CONTEXT = 128_000
MAX_OUTPUT = 4_000
encoder = tiktoken.encoding_for_model("gpt-4o")
# 计算 token
def count_tokens(messages):
total = 0
for msg in messages:
total += len(encoder.encode(msg["content"]))
return total
# 裁剪短期记忆
def trim_messages(messages):
while count_tokens(messages) > MAX_CONTEXT - MAX_OUTPUT:
messages.pop(1) # 删除最早消息
return messages
# 存储长期记忆
def store_long_term_memory(user_id, text):
embedding_response = client.embeddings.create(
model="text-embedding-3-large",
input=text
)
vector = embedding_response.data[0].embedding
index.upsert(
vectors=[{
"id": f"{user_id}-{int(os.times().system)}",
"values": vector,
"metadata": {"text": text, "userId": user_id}
}]
)
# 检索长期记忆
def retrieve_long_term_memory(user_id, query, top_k=5):
embedding_response = client.embeddings.create(
model="text-embedding-3-large",
input=query
)
vector = embedding_response.data[0].embedding
search_response = index.query(
vector=vector,
top_k=top_k,
include_metadata=True,
filter={"userId": user_id}
)
return [match['metadata']['text'] for match in search_response['matches']]
# 主聊天流程
def chat(user_id, user_message):
long_term_memory = retrieve_long_term_memory(user_id, user_message)
messages = [{"role": "system", "content": "你是一个 helpful assistant"}]
if long_term_memory:
messages.append({
"role": "system",
"content": f"用户的长期记忆信息:\n{chr(10).join(long_term_memory)}"
})
messages.append({"role": "user", "content": user_message})
messages = trim_messages(messages)
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
max_tokens=MAX_OUTPUT
)
answer = response.choices[0].message["content"]
print("AI:", answer)
# 可选:把新信息存入长期记忆
store_long_term_memory(user_id, user_message)
# --- 使用示例 ---
user_id = "user123"
store_long_term_memory(user_id, "我喜欢美式咖啡")
store_long_term_memory(user_id, "我正在学习 JavaScript")
chat(user_id, "给我推荐一款咖啡?")
chat(user_id, "帮我讲一下 JavaScript 的事件循环")
java
java
import com.theokanning.openai.OpenAiService;
import com.theokanning.openai.completion.chat.*;
import com.github.acheong08.tiktoken.Tiktoken;
import com.github.acheong08.tiktoken.Encoding;
import com.pinecone.PineconeClient; // 假设你用 Pinecone Java SDK
import java.util.ArrayList;
import java.util.List;
public class LongTermMemoryChat {
private static final int MAX_CONTEXT = 128_000;
private static final int MAX_OUTPUT = 4_000;
public static void main(String[] args) {
OpenAiService client = new OpenAiService(System.getenv("OPENAI_API_KEY"));
// 初始化 tokenizer
Encoding encoder = Tiktoken.encodingForModel("gpt-4o");
// 初始化 Pinecone (长期记忆)
PineconeClient pinecone = new PineconeClient(System.getenv("PINECONE_API_KEY"));
var index = pinecone.Index("user-memory");
String userId = "user123";
// 存储长期记忆
storeLongTermMemory(client, index, userId, "我喜欢美式咖啡");
storeLongTermMemory(client, index, userId, "我正在学习 JavaScript");
// 对话
chat(client, encoder, index, userId, "给我推荐一款咖啡?");
chat(client, encoder, index, userId, "帮我讲一下 JavaScript 的事件循环");
}
// 存储长期记忆
public static void storeLongTermMemory(OpenAiService client, Object index, String userId, String text) {
var embeddingResponse = client.createEmbeddings(
OpenAiService.EmbeddingsRequest.builder()
.model("text-embedding-3-large")
.input(text)
.build()
);
var vector = embeddingResponse.getData().get(0).getEmbedding();
// upsert 向量到 index
// index.upsert(...) // 这里具体调用看你使用的 SDK
}
// 检索长期记忆
public static List<String> retrieveLongTermMemory(OpenAiService client, Object index, String userId, String query, int topK) {
var embeddingResponse = client.createEmbeddings(
OpenAiService.EmbeddingsRequest.builder()
.model("text-embedding-3-large")
.input(query)
.build()
);
var vector = embeddingResponse.getData().get(0).getEmbedding();
// 搜索向量库
List<String> results = new ArrayList<>();
// 假设搜索返回 metadata.text
// results.add(...);
return results;
}
// 裁剪短期记忆
public static List<ChatMessage> trimMessages(List<ChatMessage> messages, Encoding encoder) {
while (countTokens(messages, encoder) > MAX_CONTEXT - MAX_OUTPUT) {
messages.remove(1);
}
return messages;
}
public static int countTokens(List<ChatMessage> messages, Encoding encoder) {
int total = 0;
for (ChatMessage msg : messages) {
total += encoder.encode(msg.getContent()).size();
}
return total;
}
// 主聊天流程
public static void chat(OpenAiService client, Encoding encoder, Object index, String userId, String userMessage) {
List<String> longTermMemory = retrieveLongTermMemory(client, index, userId, userMessage, 5);
List<ChatMessage> messages = new ArrayList<>();
messages.add(new ChatMessage("system", "你是一个 helpful assistant"));
if (!longTermMemory.isEmpty()) {
messages.add(new ChatMessage("system", "用户的长期记忆信息:\n" + String.join("\n", longTermMemory)));
}
messages.add(new ChatMessage("user", userMessage));
messages = trimMessages(messages, encoder);
ChatCompletionRequest request = ChatCompletionRequest.builder()
.model("gpt-4o")
.messages(messages)
.maxTokens(MAX_OUTPUT)
.build();
ChatCompletionResult result = client.createChatCompletion(request);
System.out.println("AI: " + result.getChoices().get(0).getMessage().getContent());
// 可选:把用户新消息存入长期记忆
storeLongTermMemory(client, index, userId, userMessage);
}
}
AI系统的完整记忆架构
真实 AI 产品一般是这样设计:
markdown
用户输入
│
▼
┌───────────┐
│ 短期记忆 │
│ Context │
└───────────┘
│
▼
┌───────────┐
│ 长期记忆 │
│ Vector DB │
└───────────┘
│
▼
Prompt
│
▼
大模型
│
▼
回复
短期记忆 vs 长期记忆(核心区别)
| 短期记忆 | 长期记忆 | |
|---|---|---|
| 本质 | Context Window | 外部存储 |
| 生命周期 | 当前对话 | 永久 |
| 存储位置 | Prompt | DB / Vector DB |
| 大小 | 有限制 | 几乎无限 |
| 用途 | 上下文理解 | 知识/用户记忆 |
总结来说:
短期记忆是模型的"脑子",长期记忆是模型的"外挂硬盘"。
AI工程里的经典组合
真实 AI 应用基本是这三层:
diff
短期记忆
+ 长期记忆
+ 工具调用
也就是:
Conversation Memory
RAG Memory
Tool Memory
很多 AI Agent 架构都是这样。
为什么大模型一定要有长期记忆
大模型本身是 无状态的
- 大模型 API(如 GPT-4o、Claude)本身不会存储任何用户对话。
- 每次请求都是 全新的计算 ,短期记忆只能通过 上下文传递。
- 如果没有长期记忆,模型就"每次都是新鲜人",无法延续跨会话的知识。
举例:
- 今天用户说:我喜欢美式咖啡
- 明天用户问:给我推荐咖啡
- 如果没有长期记忆,模型不知道用户偏好,只能随机推荐
Context Window 有限制
- GPT-4o context window ≈ 128k tokens
- 对话过长或长期使用时,旧消息会被裁剪
- 这导致 短期记忆只能保留最近几轮对话
- 长期记忆才能保存核心信息,避免重要数据被丢掉
支撑个性化和用户体验
长期记忆让模型能做到:
- 记住用户偏好
- 记住历史任务和问题
- 提供连续性和个性化回答
例如:
- "小王喜欢喝美式咖啡" → 明天直接推荐冷萃
- "用户之前问过 JavaScript 事件循环" → 下次能直接接着讲
支持 RAG(Retrieval-Augmented Generation)
- 大模型知识有限(截止训练时间)
- 用户提供的长期数据不能全部塞进 Context Window
- 解决方案:把长期知识 存储到向量数据库或文档库
- 查询时再检索,结合当前对话生成回答
流程:
用户问题 → 检索长期记忆 → 拼入 Prompt → 模型生成回答
这就是现代大模型系统必须做长期记忆的根本原因。
工程成本角度
-
如果只靠短期记忆:
- 大量信息会被丢掉
- 每次都要重复提供上下文
- 用户体验差
-
长期记忆 + RAG:
- 数据可以压缩、摘要、索引
- 只检索相关信息
- 节省 token 成本和计算资源
总结
- 短期记忆可以理解为让模型记住当前session内的历史会话
- 长期记忆可以理解为让模型除了当前session外,其他session里的历史对话
- 一般的ai产品应用会同时具备长期记忆和短期记忆
- 短期记忆的实现方式只需要把当前session的历史记录摘要汇总到prompt里即可
- 长期记忆需要通过RAG功能把相关内容向量匹配出来再汇总到prompt
最后
如果对你有用的话
