让大模型“记住你”:LangChain 中的对话记忆机制实战

大语言模型(LLM)本质上是无状态的------每一次 API 调用都像一次全新的对话,模型对之前的交互一无所知。这种设计虽保证了接口的简洁与可扩展性,却也带来了明显的局限:无法支持多轮连贯的对话体验 。用户说"我叫张三",下一句问"我叫什么名字?",若不传递上下文,模型只能回答"我不知道"。要让 AI 真正具备"记忆",开发者必须主动维护对话历史,并将其作为输入的一部分传给模型。而 LangChain 提供的 RunnableWithMessageHistory 机制,正是解决这一问题的工程化方案。

无状态调用的局限

最基础的 LLM 调用方式如下:

ini 复制代码
const res1 = await model.invoke('我叫张三,喜欢喝白兰地');
const res2 = await model.invoke('我叫什么名字?');
console.log(res2.content); // 模型无法回答,因无上下文

两次调用彼此独立,模型无法建立关联。这就像每次见面都忘记对方是谁,显然无法支撑真实的对话场景。要实现连贯交互,必须将之前的对话记录(messages)一并传入。

手动维护消息历史:可行但繁琐

一种朴素的做法是手动维护一个消息数组:

ini 复制代码
const messages = [
  { role: 'user', content: '我叫张三,喜欢喝白兰地' },
  { role: 'assistant', content: '好的,张三!' },
  { role: 'user', content: '我叫什么名字?' }
];
// 将 messages 作为 prompt 的一部分传给模型

然而,这种方式存在明显问题:随着对话轮次增加,消息长度呈"滚雪球"式增长,迅速消耗大量 token,不仅增加成本,还可能超出模型上下文窗口限制。更关键的是,开发者需自行管理存储、截断、会话隔离等逻辑,极易出错。

LangChain 的解决方案:内置记忆模块

LangChain 通过 RunnableWithMessageHistory 抽象,将"带记忆的对话"封装为一个可复用的运行单元。配合 InMemoryChatMessageHistory,可轻松实现会话级记忆:

dart 复制代码
const messageHistory = new InMemoryChatMessageHistory();
const chain = new RunnableWithMessageHistory({
  runnable,
  getMessageHistory: async () => messageHistory,
  inputMessagesKey: 'input',
  historyMessagesKey: 'history',
});

这里,runnable 是原始的提示词+模型链,而 RunnableWithMessageHistory 在其前后自动注入和更新对话历史。开发者只需关注当前输入,历史管理由框架自动完成。

构建带记忆的对话链

完整的流程包含系统提示、历史占位符和用户输入三部分:

css 复制代码
const prompt = ChatPromptTemplate.fromMessages([  ['system', "你是一个有记忆的助手"],
  ['placeholder', "{history}"],
  ['human', "{input}"]
]);
  • system 消息设定角色;
  • {history} 占位符由 LangChain 自动替换为过往对话;
  • {input} 接收当前用户提问。

当调用 chain.invoke() 时,框架会:

  1. messageHistory 读取已有消息;
  2. 将其插入 prompt{history} 位置;
  3. 调用模型生成回复;
  4. 将新对话(用户输入 + 模型回复)存回 messageHistory

会话隔离与实际效果

通过 sessionId 可区分不同用户的对话上下文:

css 复制代码
await chain.invoke({ input: '我叫张三...' }, { configurable: { sessionId: 'user123' } });
await chain.invoke({ input: '我叫什么名字?' }, { configurable: { sessionId: 'user123' } });

第二次调用时,模型能准确回答"你叫张三",因为它接收到了完整的对话历史。整个过程对开发者透明,无需手动拼接消息。

内存存储的权衡与扩展

示例中使用 InMemoryChatMessageHistory 将对话暂存于内存,适合演示或短期会话。但在生产环境中,通常需替换为持久化存储(如 Redis、数据库),并通过自定义 getMessageHistory 函数按 sessionId 加载历史。LangChain 的设计允许无缝切换底层存储,保持上层逻辑不变。

此外,为避免 token 耗尽,还可结合摘要记忆 (Summary Memory)或滑动窗口策略:当历史过长时,自动压缩旧对话或仅保留最近 N 轮。这些高级功能同样可通过 LangChain 的 memory 模块实现。

工程价值:从"问答机"到"智能体"

引入记忆机制后,AI 应用的能力边界显著拓展:

  • 个性化服务:记住用户偏好、身份信息;
  • 任务延续:在多步骤操作中保持上下文(如订票、调试);
  • 情感连贯:维持语气与风格的一致性。

更重要的是,这种能力以声明式、模块化 的方式集成,不破坏原有代码结构。开发者无需重写整个调用逻辑,只需将普通链包装为 RunnableWithMessageHistory,即可获得记忆能力。

结语

让大模型"记住"用户,不是魔法,而是工程。LangChain 通过抽象对话历史的管理,将复杂的上下文维护转化为简单的配置选项。这不仅降低了多轮对话的实现门槛,也为构建真正智能、连贯、个性化的 AI 应用铺平了道路。在 AI 从工具走向伙伴的进程中,记忆,正是建立信任与深度交互的第一步。

相关推荐
qfljg2 小时前
langchain usage
langchain
kjkdd5 小时前
6.1 核心组件(Agent)
python·ai·语言模型·langchain·ai编程
渣渣苏10 小时前
Langchain实战快速入门
人工智能·python·langchain
小天呐10 小时前
01—langchain 架构
langchain
香芋Yu13 小时前
【LangChain1.0】第九篇 Agent 架构设计
langchain·agent·架构设计
kjkdd14 小时前
5. LangChain设计理念和发展历程
python·语言模型·langchain·ai编程
ASKED_20191 天前
Langchain学习笔记一 -基础模块以及架构概览
笔记·学习·langchain
zhengfei6111 天前
【AI平台】- 基于大模型的知识库与知识图谱智能体开发平台
vue.js·语言模型·langchain·知识图谱·多分类
玄同7651 天前
LangChain 1.0 模型接口:多厂商集成与统一调用
开发语言·人工智能·python·langchain·知识图谱·rag·智能体
Bruk.Liu1 天前
(LangChain实战12):LangChain中的新型Chain之create_sql_query_chain
数据库·人工智能·sql·langchain