让大语言模型拥有“记忆”:多轮对话与 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/deepseek 和 RunnableWithMessageHistory 的完整示例:

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 类型,如 BufferWindowMemory、SummaryMemory、VectorStoreRetrieverMemory 等,可根据场景灵活选择。


五、结语

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

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

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

相关推荐
子兮曰3 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰3 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万3 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝3 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋3 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
1点东西3 天前
做了近两年的Agent开发,其实真正要学的就是这五件事
llm·agent·ai编程
卡布鲁3 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
染指11103 天前
122.Agent-LangChain核心组件-中间件-动态提示词(dynamic_promapt)
人工智能·langchain·agent·agents
汉堡大王95273 天前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
程序猿编码3 天前
告别改源码适配模型:纯 C++ 可配置 LLM 推理引擎,全格式全结构兼容
c++·大模型·llm·推理引擎