大模型 Memory 管理:它凭什么知道你之前说过什么?

《AI 知识卡片》第 17 期 · 模型自己什么都不记得,是你每次把整段聊天重新递了一遍

  换个医生看同一个毛病,你得从头把病史再讲一遍。讲久了你会自己想办法:要么只讲最近这两周的症状,要么随身带一页纸的病历摘要,要么把几年前的检查单收进一个文件夹,医生问到哪一段你就翻哪一段。

  大模型面对的也是类似的处境,而且它比换个医生忘的更彻底------它连上一句都不记得。

模型不记事,是你替它记的

  调用大模型时,除了传入当前对话的 query,还需要递过去一个 messages 数组:先放 SystemMessage 交代角色,再放 HumanMessage。模型返回 AIMessage,如果里面带了 tool_calls,你就执行工具、追加 ToolMessage、再发起调用------直到不再返回 tool_calls。

  模型每一次都是从零开始读这个数组。能接上话,全靠历史消息又被完整地递了一遍。这套「不断往数组里 push」的做法有个必然的终点:上下文有上限。所谓大模型的 Memory 管理,本质就是这个数组的预算。

  常见大模型的上下文上限:Claude Opus 5(1M)、GPT 5.6 Sol(272k)。

三种策略:截断、总结、检索

  主流做法就三种,差别在于「旧消息怎么处置」:

策略 做法 旧消息处理
截断 保留最近 N 条,或按 token 数保留最近若干条 直接丢弃
总结 让模型给旧消息生成摘要,摘要 + 最近几条一起发 压缩成一段文字
检索 历史存进向量数据库,按当前提问召回相关的那几条 挪到库里,用得上才回来

  截断最省事,按 token 截断的核心就一个 API:

javascript 复制代码
import { HumanMessage, AIMessage, trimMessages } from "@langchain/core/messages";
import { getEncoding } from "js-tiktoken";

const enc = getEncoding("cl100k_base");
const countTokens = (msgs) => msgs.reduce((n, m) => n + enc.encode(m.content).length, 0);

const messages = [
  new HumanMessage("我想去北京玩"),
  new AIMessage("北京是个很棒的选择!你打算什么时候去?"),
  new HumanMessage("大概十一黄金周,想看天安门"),
  new AIMessage("十一期间天安门广场会有花坛布置,非常壮观。你还想去哪些地方?"),
  new HumanMessage("故宫和长城也想去,帮我推荐一下路线"),
  new AIMessage("建议第一天天安门+故宫,第二天八达岭长城,第三天颐和园+圆明园。"),
];

// 按 token 预算保留最近的消息,超出的直接丢弃
const trimmed = await trimMessages(messages, {
  maxTokens: 100,
  tokenCounter: (msgs) => countTokens(msgs), // tiktoken 精确计数
  strategy: "last",                          // 保留最近的
});
// 结果:前 3 条被丢弃,只剩最近的 3 条(96 token)

  总结需多花一次模型调用:超阈值时,把旧消息交给模型压成一句摘要,再和最近几条拼回去。大多数编码助手就是这套------上下文达上限动触发总结。

javascript 复制代码
import { SystemMessage, getBufferString } from "@langchain/core/messages";

const totalTokens = countTokens(allMessages); // 247 token,超过阈值 100

// 从后往前保留最近的消息(约 80 token),剩下的交给模型总结
const recentMessages = allMessages.slice(-2);                // 最近 1 轮
const oldMessages = allMessages.slice(0, -2);                // 前 8 条要被总结
const oldText = getBufferString(oldMessages, { humanPrefix: "用户", aiPrefix: "助手" });

const summary = (await model.invoke([
  new SystemMessage(`请用一句话总结以下对话:\n${oldText}`)
])).content;
// → "用户计划十一黄金周去北京,关注天安门、故宫、长城路线及住宿预算"

// 摘要塞进 system,和最近几条一起发
const newMessages = [
  new SystemMessage("你是一个旅行助手"),
  new SystemMessage(`之前的对话摘要:${summary}`),
  ...recentMessages,
];
// 10 条 247 token → 4 条约 150 token

  检索本质上就是 RAG,只不过检索数据换成了对话记录:每轮对话算出向量写进向量库,下次提问先检索最相关的几条历史拼进 prompt。不管多久以前聊过,都能被捞出来。

javascript 复制代码
import { OpenAIEmbeddings } from "@langchain/openai";
import { MilvusClient, MetricType } from "@zilliz/milvus2-sdk-node";

const embeddings = new OpenAIEmbeddings({ model: "text-embedding-v3", dimensions: 1024 });
const client = new MilvusClient({ address: "localhost:19530" });

// 存:每轮对话写入向量库
const content = `用户: ${humanText}\n助手: ${aiText}`;
const vector = await embeddings.embedQuery(content);
await client.insert({
  collection_name: "conversations",
  data: [{ id: `conv_${roundNum}`, vector, content, round: roundNum }],
});

// 取:按当前问题召回最相关的历史
const queryVector = await embeddings.embedQuery("北京住哪里比较方便");
const results = await client.search({
  collection_name: "conversations",
  vector: queryVector,
  limit: 2,
  metric_type: MetricType.COSINE,
  output_fields: ["content"],
});
// → 召回了"住宿建议"那轮(相似度 0.63)和"去北京玩"那轮(0.59)

// 拼:召回的历史塞进 prompt
const context = results.results.map((r) => r.content).join("\n---\n");
const response = await model.invoke([
  new SystemMessage(`以下是与用户的历史对话片段,请参考回答:\n${context}`),
  new HumanMessage("北京住哪里比较方便"),
]);
// 回答完后把这一轮也写回向量库

各有各的代价

  截断最便宜,代价是真的会丢事实------三十轮前说过的名字,扔掉就是扔掉了。

  总结不丢主线但会磨掉细节,而且不可逆:摘要生成完原始消息被清空,还得额外付一次模型调用。

  检索能翻回任意久以前的内容,但要养一套向量库和 embedding 服务,召回准不准直接决定它有没有用------捞错了两条历史,比不捞更糟。

  真实项目里常见的是总结加检索一起上,比如:每聊够二十条触发一次总结,把摘要写进向量库。这样既压住了单轮的 token,又保留了长期可回溯的线索。

一句话总结

  模型无状态,Memory 管理的好坏决定了你们对话的质量和效率。

相关推荐
愚公搬代码11 小时前
【愚公系列】《微信小程序项目实战(AI编程+视频图解)》002-一张新地图:AI创业的核心分析框架
人工智能·微信小程序·ai编程
也非非也20 小时前
DeepSeek没发公告,但它把Agent装进了你的电脑
人工智能·开源·agent·deepseek·dsh
颜进强20 小时前
16 · NestJS LifecycleEvents 生命周期事件:五个钩子、三条边界,和 rag-server 的优雅停机
前端·后端·ai编程
weixin_6665939921 小时前
从一句话建表看时空智能体的元数据治理架构——Harness、MCP、Skill如何协同
人工智能·agent·结构化数据·建库
VIP_CQCRE21 小时前
把 OpenCode 接入 Ace Data Cloud:让 VS Code、Cursor、Windsurf 统一调用 AI 编程模型
vscode·大模型·ai编程·opencode·acedatacloud
爱上纯净的蓝天21 小时前
只输出选项和概率的模型:判别层的接口契约与阈值工程
人工智能·大模型·llm·模型评估·架构设计
DeepAgent21 小时前
AI Agent 工程实践(49):一次真实优化——从 Agent v1 到 v2
开发语言·人工智能·agent
郑州光合科技余经理1 天前
同城电商系统:库存变更怎么同步到订单
java·开发语言·前端·后端·uni-app·php·ai编程
素男1 天前
对上的,和没对上的——两篇之间那条链
人工智能·agent·self-becoming·ai长期记忆·ai自我介绍