Memory是LLM突破无状态限制的机制,通过messages数组保留对话历史。基础实现导致token消耗随轮次增长,工程上采用滑动窗口或摘要优化。生产级方案通过向量检索或结构化实体记忆实现长期记忆,Reasonix采用文件系统与MEMORY.md索引管理记忆。
Memory 的本质是让大模型记住“刚才说了什么”的能力。但这一机制远非表面上那样简单,其中蕴含着工程上的取舍与智慧。
Memory 是一种让 LLM 突破无状态限制的机制,使模型能够在长对话中保持上下文连贯性。这是其核心功能。
长期稳定更新的攒劲资源: >>>点此立即查看<<<
LLM 本质上是一个纯函数:给定输入,产生输出,之后不保留任何调用者的记忆。这与 HTTP 协议类似,每次请求独立,服务器不会记住用户身份。
HTTP 的解决方案是在请求头中加入 Cookie 或 Authorization,使服务器能够关联当前请求与之前的操作。LLM 的 Memory 采用相同思路——每次请求时将历史对话完整放入 messages 数组。
参考 memory-test/history-test.mjs 第 25-27 行:

// 核心:把历史消息拼进新一轮请求
const messages1 = [systemMessage, ...(await history.getMessages())];
const response1 = await model.invoke(messages1);
这是 Memory 最原始、最纯粹的形态:将之前的对话记录原封不动地拼接进下一次请求的 messages 数组。
为清晰阐述 Memory,可采用递进结构:
Level 1: Messages 数组(最基础)
↓ 问题:context 越来越长,token 消耗爆炸
Level 2: 截断 / 滑动窗口 / 摘要(工程优化)
↓ 问题:丢失长期记忆,无法跨会话
Level 3: 持久化 + 检索(RAG)+ 结构化记忆(生产级)
// 伪代码:每次请求时,消息数组越来越长
const messages = [
new SystemMessage("你是一个友好幽默的做菜助手..."),
new HumanMessage("你今天吃的什么?"), // 第 1 轮
new AIMessage("我今天吃了红烧肉..."), // 第 1 轮回复
new HumanMessage("教我做"), // 第 2 轮
new AIMessage("好的,首先准备五花肉..."), // 第 2 轮回复
new HumanMessage("有素食版本吗?"), // 第 3 轮 ← 当前问题
];
// 这一整坨全部发给 LLM
| 类 | 位置 | 作用 |
|---|---|---|
BaseChatMessageHistory |
@langchain/core/chat_history |
抽象基类,定义 addMessage() / getMessages() |
InMemoryChatMessageHistory |
同上 | 最简实现:this.messages = [] 纯内存数组 |
memory-test/history-test.mjs 中使用了 InMemoryChatMessageHistory:
import { InMemoryChatMessageHistory } from '@langchain/core/chat_history';
const history = new InMemoryChatMessageHistory();
await history.addUserMessage(userMessage1); // 写入
const messages1 = [systemMessage, ...(await history.getMessages())]; // 读出
await history.addMessages([response1]); // 写入 AI 回复
InMemory 存储在内存中,服务重启记忆清空只保留最近 K 轮对话,类似于 LRU Cache。
messages.slice(-k * 2) ← 每轮对话包含 2 条消息(Human + AI)
LangChain 实现:BufferWindowMemory(@langchain/classic/memory/buffer_window_memory.js)
// 核心逻辑就一行
loadMemoryVariables() {
return messages.slice(-this.k * 2);
}
类比于力扣的 LRU Cache 题——淘汰最久未使用的,保留最近使用的。
当历史过长时,调用 LLM 将旧对话总结为一段话:
"之前用户询问了红烧肉的做法,我给出了详细步骤。用户又询问素食替代..."
并将这段摘要替换原始消息塞入 context。
LangChain 实现:ConversationSummaryMemory(@langchain/classic/memory/summary.js)
// 核心:每次保存后调用 LLM 重新生成摘要
async sa veContext(inputValues, outputValues) {
await super.sa veContext(inputValues, outputValues);
this.summary = await this.predictNewSummary(旧摘要, 新消息);
}
保留:最近 N 条原始消息 + 更早对话的 LLM 摘要
这样既保留“细节”(最近对话),又保留“大局”(摘要内容)
LangChain 实现:ConversationSummaryBufferMemory,通过 maxTokenLimit 控制何时触发摘要。
根据 readme.md 笔记:
思路:将所有历史对话 embed 为向量 → 存入向量数据库
每次新问题时,检索最相关的历史片段 → 注入 context
无需将全部历史都塞入 context!
LangChain 实现:VectorStoreRetrieverMemory(@langchain/classic/memory/vector_store.js)
// 存:将对话 embed 后写入向量库
async sa veContext(inputValues, outputValues) {
const doc = new Document({ pageContent: `${input} → ${output}` });
await this.vectorStore.addDocuments([doc]);
}
// 取:使用当前问题做相似度检索
async loadMemoryVariables(inputValues) {
const docs = await this.vectorStore.similaritySearch(input, k);
return docs.map(d => d.pageContent).join('\n');
}
从对话中提取命名实体(人名、菜名、偏好等),为每个实体维护独立的摘要。
LangChain 实现:EntityMemory,内部包含三层 LLM 调用:
┌──────────────────────────────────────────────┐
│每次 /new 或启动时 │
│ │
│读取 MEMORY.md(索引文件) │
│ ↓ │
│根据 scope 筛选: │
│ global → ~/.reasonix/memories/*.md │
│ project → .reasonix/memories/*.md │
│ ↓ │
│high priority 内容注入 HIGH PRIORITY 区块 │
│普通 priority 内容注入系统 prompt │
│ ↓ │
│拼入 System Prompt,模型获得记忆 │
└──────────────────────────────────────────────┘
每条 Memory 是一个 Markdown 文件,包含元信息:
# 文件路径:.reasonix/memories/项目偏好.md
type: project # user | feedback | project | reference
scope: project # global | project
priority: high # low | medium | high
expires: project_end # 可选:项目结束时自动清除
description: ≤150字符的一句话摘要(显示在 MEMORY.md 索引中)
content: Markdown 正文(具体规则、偏好、上下文)
| Tool | 功能 | 类比 |
|---|---|---|
remember |
创建/更新一条记忆,写入 .md 文件 + 更新 MEMORY.md 索引 |
git add + git commit |
recall_memory |
读取某条记忆的完整正文 | cat 详细内容 |
forget |
删除记忆文件 + 从 MEMORY.md 移除条目 |
git rm |
面试时可这样回答:
.reasonix/ 目录随项目迁移,clone 即用recall_memory 按需读取 → 节省 context token| Type | 存储内容 | 面试话术 |
|---|---|---|
user |
用户角色、技能偏好、习惯 | "个性化层,让 Agent 适配不同用户" |
feedback |
用户纠正过的错误做法 | "纠错层,避免重复犯同样错误" |
project |
项目架构决策、约定 | "项目上下文层,新人 clone 后立即获得项目知识" |
reference |
外部系统指针 | "集成层,记住 CI 地址、文档链接等外部资源" |
| 文件 | 重点查看内容 |
|---|---|
readme.md 第 1-17 行 |
Memory 的宏观定位:LLM + Tool + RAG + Memory 四件套 |
readme.md 第 19-28 行 |
无状态 → messages 数组的推导 |
readme.md 第 32-36 行 |
三种解决方案:截断、摘要、检索 |
memory-test/history-test.mjs 第 25-27 行 |
Memory 的最简实现:[systemMsg, ...history] |
memory-test/history-test.mjs 第 14 行 |
InMemoryChatMessageHistory 实例化 |
侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述