让大语言模型拥有“记忆”:多轮对话与 LangChain 实践指南

让大语言模型拥有"记忆":多轮对话与 LangChain 实践指南

在当前人工智能应用开发中,大语言模型(LLM)因其强大的自然语言理解与生成能力,被广泛应用于聊天机器人、智能客服、个人助理等场景。然而,一个常见的问题是:LLM 本身是无状态的------每一次 API 调用都独立于前一次,就像每次 HTTP 请求一样,模型无法"记住"你之前说过什么。

那么,如何让 LLM 拥有"记忆",实现真正的多轮对话体验?本文将从原理出发,结合 LangChain 框架的实践代码,深入浅出地讲解这一关键技术。


一、为什么 LLM 默认没有记忆?

大语言模型(如 DeepSeek、GPT、Claude 等)在设计上遵循"输入-输出"模式。当你调用其 API 时,仅传递当前的问题或指令,模型不会自动保留之前的交互内容。例如:

ini 复制代码
ts
编辑
const res1 = await model.invoke('我叫王源,喜欢喝白兰地');
const res2 = await model.invoke('我叫什么名字?');

第二次调用时,模型完全不知道"王源"是谁,因此很可能回答:"我不知道你的名字。"

这是因为两次调用之间没有任何上下文传递。


二、实现"记忆"的核心思路:维护对话历史

要让 LLM 表现出"有记忆"的行为,最直接的方法是:在每次请求中显式传入完整的对话历史 。通常以 messages 数组的形式组织:

css 复制代码
json
编辑
[  {"role": "user", "content": "我叫王源,喜欢喝白兰地"},  {"role": "assistant", "content": "很高兴认识你,王源!白兰地是很优雅的选择。"},  {"role": "user", "content": "你知道我是谁吗?"}]

模型通过分析整个对话上下文,就能准确回答:"你是王源,喜欢喝白兰地。"

但这种方法存在明显问题:随着对话轮次增加,输入 token 数量不断膨胀,不仅增加计算成本,还可能超出模型的最大上下文长度限制(如 32768 tokens)。这就像滚雪球,越滚越大。


三、LangChain 提供的解决方案:模块化记忆管理

为了解决上述问题,LangChain 等 AI 应用框架提供了专门的 Memory 模块,帮助开发者高效管理对话历史,并支持多种存储策略(内存、数据库、向量化摘要等)。

以下是一个使用 @langchain/deepseekRunnableWithMessageHistory 的完整示例:

1. 初始化模型与提示模板

javascript 复制代码
ts
编辑
import { ChatDeepSeek } from '@langchain/deepseek';
import { ChatPromptTemplate } from '@langchain/core/prompts';
import { RunnableWithMessageHistory } from '@langchain/core/runnables';
import { InMemoryChatMessageHistory } from '@langchain/core/chat_history';

const model = new ChatDeepSeek({
  model: 'deepseek-chat',
  temperature: 0,
});

// 定义包含历史记录占位符的提示模板
const prompt = ChatPromptTemplate.fromMessages([
  ['system', '你是一个有记忆的助手'],
  ['placeholder', '{history}'], // 历史消息将插入此处
  ['human', '{input}']
]);

2. 构建带记忆的可运行链

dart 复制代码
ts
编辑
const runnable = prompt.pipe(model);

// 创建内存中的对话历史存储
const messageHistory = new InMemoryChatMessageHistory();

// 封装成支持会话记忆的链
const chain = new RunnableWithMessageHistory({
  runnable,
  getMessageHistory: async () => messageHistory,
  inputMessagesKey: 'input',
  historyMessagesKey: 'history',
});

3. 执行多轮对话

php 复制代码
ts
编辑
// 第一轮:用户自我介绍
const res1 = await chain.invoke(
  { input: '我叫王源,喜欢喝白兰地' },
  { configurable: { sessionId: 'makefriend' } }
);
console.log(res1.content); // "你好,王源!白兰地确实很经典。"

// 第二轮:提问名字
const res2 = await chain.invoke(
  { input: '我叫什么名字?' },
  { configurable: { sessionId: 'makefriend' } }
);
console.log(res2.content); // "你叫王源。"

✅ 关键点:sessionId 用于区分不同用户的会话。同一个 sessionId 下的所有交互共享同一段历史。


四、进阶思考:如何优化长对话的记忆效率?

虽然内存存储简单易用,但在生产环境中,面对海量用户和长期对话,我们需要更高效的策略:

  1. 滑动窗口记忆:只保留最近 N 轮对话。
  2. 摘要压缩:定期将历史对话总结成一段摘要,替代原始记录。
  3. 向量数据库 + 语义检索:将关键信息存入向量库,按需检索相关上下文(适用于知识密集型对话)。
  4. 混合记忆:结合短期(最近几轮)+ 长期(摘要/数据库)记忆。

LangChain 已支持多种 Memory 类型,如 BufferWindowMemorySummaryMemoryVectorStoreRetrieverMemory 等,可根据场景灵活选择。


五、结语

让 LLM 拥有"记忆",本质上是将无状态的模型调用转化为有状态的会话系统。通过维护对话历史并合理控制上下文长度,我们可以在成本与体验之间取得平衡。

LangChain 等框架极大简化了这一过程,使开发者能专注于业务逻辑,而非底层状态管理。未来,随着上下文窗口的扩大和记忆机制的智能化(如自动遗忘、重点记忆),AI 助手将越来越像一个真正"记得你"的朋友。

正如我们在代码中看到的那样------当模型说出"你叫王源"时,那一刻,它仿佛真的记住了你。

相关推荐
尾善爱看海7 分钟前
不常用的浏览器 API —— Web Speech
前端
美酒没故事°1 小时前
vue3拖拽+粘贴的综合上传器
前端·javascript·typescript
山顶夕景1 小时前
【VLM】Format Decoupled Reinforcement Learning for Document OCR
大模型·llm·ocr·多模态·文档智能·vlm
jingling5552 小时前
css进阶 | 实现罐子中的水流搅拌效果
前端·css
悟能不能悟3 小时前
前端上载文件时,上载多个文件,但是一个一个调用接口,怎么实现
前端
可问春风_ren4 小时前
前端文件上传详细解析
前端·ecmascript·reactjs·js
羊小猪~~5 小时前
【QT】--文件操作
前端·数据库·c++·后端·qt·qt6.3
神云瑟瑟5 小时前
看langchain理解python中的链式调用
python·langchain·链式调用
晚风资源组6 小时前
CSS文字和图片在容器内垂直居中的简单方法
前端·css·css3
Miketutu6 小时前
Flutter学习 - 组件通信与网络请求Dio
开发语言·前端·javascript