大语言模型本身并不记得上一次对话。若应用没有把此前的聊天内容重新提交给模型,第二轮提问中的“好吃吗”就只是一个缺乏上下文的孤立问题。LangChain.js 提供了一套围绕消息历史构建的短期记忆机制,使开发者可以通过内存缓存、文件持久化、消息截断和摘要压缩等方式,为 Agent 补齐连续对话能力。掘金近日发布的技术文章《LangChain.js Agent Memory 实战(上)》系统梳理了这一工程路径,并重点讨论了 Token 预算对 Agent 行为的影响。
短期记忆的核心:消息历史管理
文章指出,大模型是无状态的,所谓连续对话,本质上是应用层保存了历史消息,并在下一次调用时将其重新放入上下文。若将 Agent 简化为“LLM + Harness”,其中 Harness 包含工具、RAG、记忆等组件,那么 Memory 就是负责“记住并管理过去信息”的部分。
LangChain.js 用消息对象表示不同角色的输入输出,包括 SystemMessage、HumanMessage 和 AIMessage。最基础的实现方式是使用 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