大模型 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 管理的好坏决定了你们对话的质量和效率。

相关推荐
孟健1 小时前
Image 2.5来了,我用双参考图跑通了连续四格故事
ai编程
杨杨杨大侠2 小时前
KV Cache 到底缓存了什么?从逐 token 生成讲到 GPU 与显存
aigc·openai·ai编程
一航jason2 小时前
GPU 推荐用吗?——8295 上 Adreno 695 的现实评估
人工智能·ai·ai编程·llama·ai-native
GISMagic2 小时前
Agent学习,写在开始之前
大数据·学习·agent
桃西西呀2 小时前
换个会话就失忆?拆开 Agent 记忆的 4 层与 4 个流派,附9个坑的自检清单
人工智能·llm·ai编程
桃西西呀2 小时前
风控模型说自己 80% 准,却漏掉了 76% 该拦的人:一次把模型评估讲透
人工智能·机器学习·llm
小虎AI生活2 小时前
腾讯文档AI工作台拆解:Agent内核进文件后,AI办公的交接问题终于有解了
ai编程
小傅哥2 小时前
Java + DDD,1:1 复刻 Deepseek Harness 项目
前端·后端·ai编程
Cx330❀3 小时前
【Linux网络】深入TCP协议:从滑动窗口到拥塞控制与性能优化全景解析
linux·开发语言·网络·tcp/ip·ai·性能优化·ai编程