[LangGraph] 短期记忆

这节课我们来介绍一下记忆。在 langgraph 中,记忆分为两种:

  • 短期记忆:Short-term Memory
  • 长期记忆:Long-term Memory

基本概念

所谓短期记忆,指的就是在某一个 thread 内持续演化的 State。只要 State 在同一个 thread_id 下被反复传入和更新,agent 就具备了多轮对话的能力。

举个例子,假设我们在 StateGraph 里定义了一个 State

type State = {
  messages: Message[];
  topic?: string;
  plan?: string;
}

State 里装的内容,本身就是短期记忆。多轮对话能成立,靠的是这三件事一起配合:

  1. State 会在同一个 thread_id 下持续存在
  2. 每一轮执行,都会把上一次的 State 传进来
  3. 新节点在旧 State 的基础上继续改

例如:

// 第 1 轮
State.messages = ["你好"]

// 第 2 轮
State.messages = ["你好", "你是谁"]

// 第 3 轮
State.messages = ["你好", "你是谁", "我们刚刚聊了什么"]

另外,这里一定要注意,短期记忆是和 thread 绑定的,也就是说:

  • thread_id = A 有一份独立的 State
  • thread_id = B 是另一份完全不同的 State

只要 thread 不同:短期记忆就不共享。这也是为什么官网说它是 short-term,因为它的生命周期是:

从 thread 开始 → 到 thread 结束

而不是:

  • 跨会话
  • 跨线程
  • 跨用户

快速上手

下面我们通过一个快速上手示例来验证:

  • 不同用户 → 不同 thread
  • 切换用户 → 短期记忆立即切换
  • 不使用 Store(长期记忆)
  • 只使用 State + MemorySaver

用户设计

用户名 密码 thread_id
aaa 111 thread-aaa
bbb 222 thread-bbb

每个用户固定使用一个 thread_id

import "dotenv/config";
import { ChatOpenAI } from "@langchain/openai";
import {
  MemorySaver,
  StateGraph,
  START,
  END,
  MessagesZodMeta,
} from "@langchain/langgraph";
import { z } from "zod/v4";
import readline from "readline-sync";
import { HumanMessage, BaseMessage } from "@langchain/core/messages";
import { registry } from "@langchain/langgraph/zod";

// 用户系统
const users = {
  aaa: { password: "111", thread_id: "thread-aaa" },
  bbb: { password: "222", thread_id: "thread-bbb" },
};

const Schema = z.object({
  messages: z.array(z.custom<BaseMessage>()).register(registry, {
    ...MessagesZodMeta,
    default: () => [],
  }),
});

type TState = z.infer<typeof Schema>;

const model = new ChatOpenAI({
  model: "gpt-4o",
  temperature: 0.5,
});

// 聊天的节点
async function chatNode(state: TState): Promise<Partial<TState>> {
  const res = await model.invoke(state.messages);

  return {
    messages: [res],
  };
}

const checkpointer = new MemorySaver();

// 图
const graph = new StateGraph(Schema)
  .addNode("chatNode", chatNode)
  .addEdge(START, "chatNode")
  .addEdge("chatNode", END)
  .compile({ checkpointer });

async function main() {
  while (true) {
    console.log("\n--- 登录 ---");
    const username = readline.question("请输入用户名:");
    const password = readline.question("请输入密码:", {
      hideEchoBack: true,
    });

    const user = users[username];

    if (!user || user.password !== password) {
      console.log("登录失败☹️");
      continue;
    }

    // 登录成功
    console.log(`✅ 登录成功,当前用户:${username}`);
    console.log("输入 /switch 可切换用户\n");

    const config = {
      configurable: {
        thread_id: user.thread_id, // 这个非常重要,这个保证了不同的用户拥有不同的state
      },
    };

    // 处理用户的对话
    while (true) {
      const input = readline.question(`${username}>`);

      if (input === "/switch") {
        console.log("切换用户中...");
        break;
      }

      // 这里只需要传入当前这一轮的输入消息
      const result = await graph.invoke(
        {
          messages: [new HumanMessage(input)],
        },
        config,
      );

      // 返回的结果就是完整的状态
      const aiMsg = result.messages.at(-1);

      console.log(`AI> ${aiMsg?.content}`);
    }
  }
}
main();


-EOF-

posted @ 2026-03-08 15:01  Zhentiw  阅读(2)  评论(0)    收藏  举报