LangChain.js 短期记忆实践:如何在无状态模型之上维持连续对话

大模型本身无状态,连续对话依赖应用层维护消息历史。LangChain.js 通过内存历史、文件持久化和上下文压缩,为 Agent 构建短期记忆能力。

大语言模型本身并不记得上一次对话。若应用没有把此前的聊天内容重新提交给模型,第二轮提问中的“好吃吗”就只是一个缺乏上下文的孤立问题。LangChain.js 提供了一套围绕消息历史构建的短期记忆机制,使开发者可以通过内存缓存、文件持久化、消息截断和摘要压缩等方式,为 Agent 补齐连续对话能力。掘金近日发布的技术文章《LangChain.js Agent Memory 实战(上)》系统梳理了这一工程路径,并重点讨论了 Token 预算对 Agent 行为的影响。

短期记忆的核心:消息历史管理

文章指出,大模型是无状态的,所谓连续对话,本质上是应用层保存了历史消息,并在下一次调用时将其重新放入上下文。若将 Agent 简化为“LLM + Harness”,其中 Harness 包含工具、RAG、记忆等组件,那么 Memory 就是负责“记住并管理过去信息”的部分。

LangChain.js 用消息对象表示不同角色的输入输出,包括 SystemMessageHumanMessageAIMessage。最基础的实现方式是使用 InMemoryChatMessageHistory,它把原本需要手动维护的消息数组封装成一个历史记录对象,提供添加、读取等基本操作。

  • 用户输入先写入 history;
  • 调用模型时,将系统提示与 history 中全部消息一起发送;
  • 模型返回的 AIMessage 必须再次写回 history,否则下一轮上下文将缺失上一轮回答。

文章特别提醒,model.invoke() 只返回本轮结果,不会自动维护历史,遗漏写回操作是初学者常见错误。

从内存到文件:跨进程保存会话

内存历史虽然简单,但只存在于当前 Node.js 进程中,应用重启后消息即丢失。为解决这一问题,文章演示了使用 FileSystemChatMessageHistory 将消息持久化到本地 JSON 文件。

该方式通过 filePath 指定存储位置,通过 sessionId 区分不同用户或会话。同一个文件可以保存多个会话记录,便于在多用户场景中隔离上下文。文件中的消息不仅包含文本内容,还包括消息类型、附加参数以及模型返回的元数据,例如 Token 用量。

与内存模式相比,文件持久化只改变了存储层接口,调用逻辑基本一致:addMessage() 负责写入,getMessages() 负责还原。这种设计让开发者可以在不改动对话流程的情况下替换底层存储。

Token 预算下的上下文压缩

文章强调,存储和管理并不等同。完整保存所有历史消息,并不意味着每次调用都应将全部消息送入模型。上下文窗口和调用成本共同构成了 Token 预算约束,Agent 需要在有限预算内选择最相关的信息。

在短期记忆阶段,常见处理方式包括消息截断与摘要压缩。前者按轮次或 Token 数保留最近对话,后者则把较早历史总结为更短文本,从而降低上下文长度。文章认为,这类机制决定了 Agent 是“记住更多”还是“用得更省”,也直接影响推理成本和回答质量。

该篇为上篇内容,主要聚焦短期记忆与压缩策略。作者还预告,下篇将进一步引入 Milvus 等向量数据库,不再把全部历史塞给模型,而是根据当前问题检索相关记忆,从而把短期记忆扩展到更长周期的记忆管理。

原创文章,作者:点点,如若转载,请注明出处:https://www.dian8dian.com/langchain-js-duan-qi-ji-yi-shi-jian-ru-he-zai-wu-zhuang-tai

Like (0)
点点的头像点点
Previous 6小时前
Next 2024年9月8日 下午8:00

相关推荐