LangChain LangGraph
Module 1 Module 2 Module 3 Module 4 Module 5 Module 6
Module 5 · Long-term Memory
1 5-1 Memory Store
2 5-2 Schema Profile
3 5-3 Schema Coll.
4 5-4 Memory Agent
SUM Module Summary
HomeLangGraphModule 5 · Long-term MemoryModule Summary
LANGGRAPH MODULE 5 · SUMMARY

Long-term Memory
Module Summary

From Store infrastructure to autonomous memory Agent, Module 5 builds a complete cross-session long-term memory system, allowing AI to truly remember every user.

4sub-course
2Memory Schema Type
3Core Store API
Persistent memory across sessions
Module learning path
Memory Store
L1 · InMemoryStore
put / search / get
Profile Schema
L2 · Single user portrait
trustcall atomic update
Collection Schema
L3 · Multiple memory lists
Incremental expansion / independent update
Memory Agent
L4 · LLM autonomous decision-making
Tool call · Dual-mode Store
Three major knowledge areas
memory infrastructure
Checkpointer vs Store: The former stores the status snapshot of the picture, and the latter stores the user's semantic memory.
InMemoryStore: Memory Store in the development stage, which can seamlessly replace the database backend
namespace design: shaped like(user_id, "memories")A tuple of , organized in memory like a file system path
put(): Write to memory, key uses UUID to maintain uniqueness
search(): Semantic retrieval, finding relevant memories based on the query string
Memory pattern design
Profile: A single Pydantic object, representing "global knowledge about the user", overwritten and updated with a fixed key
Collection: A list composed of multiple independent memories, incrementally expanded after each conversation
trustcall: Instructor-style LLM call library, responsible for extracting structured profiles from conversations
ExtractMemory schema: LLM decides which independent facts to extract in Collection scenario
Fixed key vs UUID key: Profile uses a fixed key (overwrite in place), Collection uses UUID (append does not overwrite)
autonomous memory agent
Dual mode Store:Short-term within the conversation (checkpointer) + long-term user (Store) are mounted at the same time
Tool call routing: LLM can decide independently whether to call save_memory / search_memory or reply directly.
after=["respond"]: Memory written when replying to userafterAsynchronous execution, no blocking response
task_mAIstro mode: Combined with ToDo + Profile dual memory, dynamically decides when to read and write memory
namespace_for_memory: Read user_id from config at runtime to dynamically build the namespace
Detailed explanation of 4 sub-courses
1

Memory Store

InMemoryStore · put / search / get · namespace

checkpointer only cares about "what was said in this conversation", and Store cares about "who this user is". L1 introduces a cross-thread long-term memory infrastructure that allows AI to truly remember users between sessions.

  • Checkpointer vs Store: checkpointer uses thread_id as the key to store the graph state; Store uses user_id + namespace as the key to store semantic memory. The two are complementary.
  • InMemoryStore: Memory version of Store, the API is exactly the same as the database version, switching only requires replacing one line of code
  • namespace tuple(user_id, "memories")Like file paths, isolate different users and different types of memories
  • store.put(ns, key, value): Write a memory, value is any dictionary; key determines whether to overwrite or append
  • store.search(ns, query): Semantic search, returnSearchItemList, each item contains.valueand relevance scores
Core insights:checkpointer is "working memory" and Store is "semantic memory". Analogy to humans: checkpointer is like short-term working memory (what you are thinking about at the moment), and Store is like long-term memory (knowing all the facts about a person). Both are indispensable.
In-depth explanation →
2

Memory Schema: Profile

Pydantic Profile · trustcall · Fixed key atomic updates

Profile (portrait) is for the userunitary structured cognition: Name, occupation, preferences... Automatically updated with LLM after each conversation, always keeping the latest version, overwritten with a fixed key, and always only one copy.

  • Pydantic Profile class: Define user profile fields (name, job, interests...). The fields are all Optional and have a default value of None.
  • trustcall librarycreate_extractor(llm, tools=[Profile])Encapsulate LLM, dedicated to extracting structured profiles from conversations
  • Atomic updates: The patch mode of trustcall only modifies the changed fields. Unmentioned fields retain their original values and old information will not be cleared due to new conversations.
  • Fixed key "user_profile": Profile uses a fixed string as the key and will be overwritten in place the next time it is put to ensure that there is always only one user portrait.
  • Writing timing: After the dialogue node is executed,write_memoryThe node reads the message history and calls trustcall to update the Profile.
Core insights:Profile is good for "global facts about the user" - this information isSingle and evolving(If the user changes jobs, the same Profile will be updated). If you use Collection to save Profile, it will produce a large number of duplicate and conflicting memory entries.
In-depth explanation →
3

Memory Schema: Collection

Multiple memories · UUID key · ExtractMemory · Incremental expansion

Collection storesIndependent, incrementally expandable fact list: The preferences mentioned by the user, the concepts learned, the recorded to-dos... Each memory exists independently, using UUID as the key, and does not interfere with each other.

  • ExtractMemory schema: Includingcontent: strfields, LLM decides which independent facts are worth recording from the conversation
  • UUID key: each memorystr(uuid4())As a key, ensure that new memory is appended instead of overwriting old memory.
  • trustcall batch extraction: A single conversation can extract multiple ExtractMemories at the same time and put them into the Store one by one.
  • search timing: When the call_model node starts, use the current message as the query semantics to search the Store and inject the relevant memory into the SystemMessage
  • Collection vs Profile selection: The relationship is "one-to-many" and needs to be updated independently → Collection; the relationship is "one-to-one" and the entirety is overwritten → Profile
Core insights:The power of Collection isSemantic retrieval——Instead of stuffing all memories into LLM, only the most relevant ones are retrieved based on the current conversation content. This allows the memory system to grow indefinitely without worrying about the token limit.
In-depth explanation →
4

Memory Agent: task_mAIstro

LLM autonomous decision-making · Dual-mode Store · Tool routing · Asynchronous writing

Memory writes for the first three lessons are triggered by hard-coding the nodes of the graph. L4 evolves to letLLM discretionWhen to read and write memory - Agent is invoked through tools to actively store tasks, preferences and reflections when needed, without human intervention.

  • Dual-mode Store mountingcompile(checkpointer=memory, store=in_memory_store)Mount two types of storage at the same time, and nodes can access them at the same time.
  • store injection node: Node function signature plusstore: BaseStoreParameters, LangGraph automatically injects into the current Store instance
  • tool routing: LLM can be calledsave_memory / update_memoryTools, or reply directly to users, making decisions completely independently
  • after=["respond"] asynchronous writing: The memory saving node is triggered after the respond node is completed, and the user does not need to wait for the memory writing to complete.
  • task_mAIstro mode: Collection is used for ToDo items (each task is independent), Profile (single overwrite) is used for user preferences, and one Agent manages two memories.
Core insights:From "node triggers writing memory" to "LLM decides what memory to write" is a leap from rule-driven to intelligence-driven. The Agent naturally determines "Is this message worth recording?" during the conversation, which is a key step towards a truly personalized AI assistant.
In-depth explanation →
Quick review of core concepts
conceptCore APIProblem solvedTypical scenario
InMemoryStorefrom langgraph.store.memory import InMemoryStoreCross-thread Store in memory, the API is consistent with the database versionQuickly verify memory logic during development phase
store.put()store.put(namespace, key, {"data": value})Write or overwrite a memorySave/update user portrait or to-do list
store.search()store.search(namespace, query="...")Semantically search related memories, return SearchItem listRetrieve historical memories related to the current topic before the conversation
namespace design(user_id, "memories")tupleIsolate different users and different types of memoriesMemory space isolation for multi-user systems
Profile SchemaPydantic + trustcall + fixed keySingle structured user portrait, overwritten and updated in placeRecord global facts such as user name, occupation, preferences, etc.
trustcallcreate_extractor(llm, tools=[Profile])Extract structured data from conversations, patch schema atomic updatesAutomatically update user portrait after conversation
Collection SchemaExtractMemory + UUID keyIndependent fact list, each item is stored independently, incrementally expandedRecord user preferences, study notes, and to-do tasks
Dual-mode Store mountingcompile(checkpointer=..., store=...)Enable in-session memory + cross-session memory at the same timeStandard configuration for all Memory Agent scenarios
store parameter injectiondef node(state, store: BaseStore)Node functions directly access the Store without manual passingStandard writing method for memory read and write nodes
after=[] asynchronous nodebuilder.add_edge("respond", "save_memory")Memory writes do not block responses to usersMemory update background execution in Memory Agent
Module core harvest
🧠

Both kinds of memory have their own division of labor

checkpointer stores "the context of this session" (working memory), and Store stores "persistent facts about this user" (long-term memory). The former is thread level, the latter is user level, and only when used in combination is complete.

🏷️

Schema selection determines the memory structure

Profile (fixed key + Pydantic) is suitable for "a single and evolving global fact"; Collection (UUID key + list) is suitable for "growing independent entries". Choosing the wrong Schema can cause memories to overwrite each other or explode repeatedly.

🔍

Semantic retrieval is key to scalability

store.search(query)Let the memory system store thousands of historical memories, but only retrieve the most relevant ones each time and inject LLM. The amount of memory is no longer limited by the Token window, and the system is truly scalable.

⚙️

namespace is the foundation of memory isolation

(user_id, type)The namespace design allows multi-user systems to be naturally isolated. The same Store instance can serve hundreds of users, and each user's memory is completely independent and does not interfere with each other.

🤖

LLM autonomous decision-making is a more advanced form

The evolution from "node hard coding triggers writing memory" to "LLM judging whether the value is worth recording" is a leap from rule-driven to intelligence-driven. The Agent can sense which conversation content is worth retaining for a long time. This is the core capability of a truly personalized assistant.

Asynchronous writing ensures response speed

Memory writing is placed after the respond node. The moment the user gets the reply, the memory update is performed in the background. To the user, the memory system is "transparent" - there is no perceived delay, but the memory is quietly updated after each conversation.

Return to Module 5 Home Page
Comprehensive practical projects
Project

personal study companion
Personal Learning Companion Agent

A learning assistant that can continue to understand you across multiple conversations - record your knowledge background (Profile), accumulate your study notes (Collection), and let LLM decide independently when to retrieve and retrieve memories (Memory Agent). Comprehensive use of all four core knowledge points of Module 5 presents a complete long-term memory project.

L1 InMemoryStore L2 Profile Schema L3 Collection Schema L4 Memory Agent
Project characteristics
Persistently remember user context across sessions
Profile + Collection dual memory mode
Semantic retrieval of the most relevant historical notes
LLM autonomously determines when to save memories
Memory writes are asynchronous and do not block responses
Multi-user isolation, namespace design
System architecture diagram
checkpointer (in-conversation memory) · L1
thread_id → message history snapshot
The life cycle is completed when the session ends
InMemoryStore (cross-session memory) · L1
user_id → Profile(L2)
user_id → Notes collection (L3)
LearningGraph node flow
retrieve_memories
search Profile · L2
search Notes · L3
Inject SystemMessage
START →
respond · L4
LLM call tool or reply directly
save_memory / search_memory
Autonomous routing decisions
Core Agent Node
update_profile · L2
trustcall extract Profile
Fixed key overwriting in place
save_notes · L3
Extract study note entries
UUID key append writing
after respond asynchronous execution · L4
Code block 1 / 5 Schema definition and Store initialization - L1 + L2 + L3
# ── 依赖导入 ──────────────────────────────────────────────
import uuid
from typing import Optional, Literal
from pydantic import BaseModel, Field
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
from langgraph.graph import StateGraph, START, END, MessagesState
from langgraph.store.base import BaseStore
from langgraph.store.memory import InMemoryStore   # 【L1】跨线程 Store
from langgraph.checkpoint.memory import MemorySaver   # 【L1】对话内 Checkpointer
from trustcall import create_extractor             # 【L2】结构化提取库

# ════════════════════════════════════════════════
# 【L2】Profile Schema:单一结构化用户画像
#   · 固定 key "user_profile" → 每次覆写同一份
#   · 所有字段 Optional,新对话只更新提及的字段
# ════════════════════════════════════════════════
class LearnerProfile(BaseModel):
    name:           Optional[str]       = Field(None, description="用户姓名")
    expertise_level: Optional[str]     = Field(None, description="技术水平: beginner/intermediate/advanced")
    current_topics: Optional[list[str]] = Field(None, description="正在学习的技术主题列表")
    preferred_style: Optional[str]     = Field(None, description="偏好的解释风格: concise/detailed/example-driven")
    background:     Optional[str]      = Field(None, description="职业背景或编程经验简述")

# ════════════════════════════════════════════════
# 【L3】Collection Schema:独立学习笔记条目
#   · 每条笔记独立,UUID key → 追加不覆盖
#   · LLM 决定对话中哪些知识点值得记录
# ════════════════════════════════════════════════
class LearningNote(BaseModel):
    topic:   str = Field(description="知识点所属主题(如 LangGraph、Python、ML)")
    content: str = Field(description="具体知识点内容或用户的理解/疑问")
    type:    Literal["concept", "question", "insight"] = Field(
        description="记忆类型:concept=新概念, question=待解答疑问, insight=用户洞察"
    )

# ── 【L1】初始化双存储 ──────────────────────────────────
in_memory_store = InMemoryStore()   # 跨会话长期记忆
checkpointer    = MemorySaver()     # 对话内短期记忆(断点/时间旅行)
llm             = ChatOpenAI(model="gpt-4o-mini")

# ── 【L2】trustcall 提取器:从对话历史提取 LearnerProfile ──
profile_extractor = create_extractor(llm, tools=[LearnerProfile], tool_choice="LearnerProfile")

# ── 【L3】trustcall 提取器:从对话提取多条 LearningNote ──
notes_extractor = create_extractor(llm, tools=[LearningNote], tool_choice="LearningNote")
L2 · Profile Why are they all Optional?

Users don't tell everything in one conversation.Optional + trustcall patch modeGuarantee: If name is mentioned in the conversation, only name will be updated; if expertise_level is not mentioned, the last value will be retained. Avoid the problem of "new conversation clears old Profile".

L3 · Collection’s type field value

LearningNote’s type field (concept/question/insight) letsStore’s semantic retrieval is more accurate——You can search the user's question type notes individually to find all unanswered questions, instead of searching all the notes together.

Code block 2 / 5 Memory retrieval node - L1 + L2 + L3
class LearnerState(MessagesState):
    user_id:        str     # 用户唯一 ID,用于构建 namespace
    profile_ctx:    str     # 从 Store 读出的 Profile 摘要,注入 System
    notes_ctx:      str     # 从 Store 检索出的相关笔记,注入 System

# ════════════════════════════════════════════════
# 【L1 + L2 + L3】retrieve_memories 节点
#   · 接收 store 参数(LangGraph 自动注入)
#   · 从 Store 读取 Profile(L2)和相关笔记(L3)
#   · 将记忆格式化后存入 State,后续节点直接使用
# ════════════════════════════════════════════════
def retrieve_memories(state: LearnerState, store: BaseStore):
    user_id   = state["user_id"]
    last_msg  = state["messages"][-1].content

    # ── 【L2】读取用户画像(Profile)────────────────────────
    # 用固定 key "user_profile" get 单条记忆,无则返回 None
    profile_ns = (user_id, "profile")
    profile_item = store.get(profile_ns, "user_profile")
    profile_ctx = ""
    if profile_item:
        p = profile_item.value
        profile_ctx = f"用户画像:姓名={p.get('name','未知')}, 水平={p.get('expertise_level','未知')}, "
                      f"当前主题={p.get('current_topics','')}, 偏好风格={p.get('preferred_style','')}"

    # ── 【L3】语义检索最相关学习笔记(Collection)──────────
    # 用当前问题作为 query,找到最相关的 3 条历史笔记
    notes_ns    = (user_id, "notes")
    results     = store.search(notes_ns, query=last_msg, limit=3)
    notes_ctx   = ""
    if results:
        notes_lines = [f"[{r.value['type']}] {r.value['topic']}: {r.value['content']}"
                       for r in results]
        notes_ctx = "相关学习记录:\n" + "\n".join(notes_lines)

    return {"profile_ctx": profile_ctx, "notes_ctx": notes_ctx}
L1 · store parameters are automatically injected

Node function signature writingstore: BaseStore, LangGraph incompile(store=...)time meetingAutomatically inject Store instancesto each node that declares this parameter. No need to manually pass or staging Store instances through State.

L3 · The difference between store.get vs store.search

Profile usestore.get(ns, "user_profile")——Exact key lookup, returns a single memory or None. For Collectionstore.search(ns, query=...)——Semantic search, returns a list sorted by relevance. Both methods are used together in the same node.

Code Block 3 / 5 Memory Agent response node - L4
# ════════════════════════════════════════════════
# 【L4】respond 节点:Memory Agent 的核心
#   · LLM 读取 Profile + 相关笔记,个性化作答
#   · 可选地调用 save_memory 工具主动触发记忆保存
#   · 也可以主动调用 search_memory 工具获取更多记忆
# ════════════════════════════════════════════════

# ── 【L4】供 LLM 调用的记忆工具 ──────────────────────────
from langchain_core.tools import tool

@tool
def save_memory(topic: str, content: str, memory_type: str) -> str:
    """将重要知识点或用户信息保存到长期记忆。
    用于:用户分享了重要背景信息、学到了新概念、提出了关键疑问时。
    memory_type: 'concept' / 'question' / 'insight'
    """
    return f"已请求保存记忆:[{memory_type}] {topic}: {content}"

@tool
def search_memory(query: str) -> str:
    """主动检索用户的历史学习记录。
    用于:需要回顾用户之前的问题或理解时。
    """
    return f"已请求检索记忆:{query}"

agent_llm = llm.bind_tools([save_memory, search_memory])

def respond(state: LearnerState):
    """【L4】核心回复节点:根据用户画像和历史笔记个性化作答"""
    profile_ctx = state.get("profile_ctx", "")
    notes_ctx   = state.get("notes_ctx", "")

    system_parts = ["你是一个个性化学习伴侣,根据用户的知识背景和历史笔记定制回答。"]
    if profile_ctx:
        system_parts.append(f"\n{profile_ctx}")
    if notes_ctx:
        system_parts.append(f"\n{notes_ctx}")
    system_parts.append(
        "\n当对话中出现值得记录的新知识点、用户疑问或重要背景信息时,"
        "请主动调用 save_memory 工具。这些记录将在未来对话中帮助你更好地辅导用户。"
    )

    response = agent_llm.invoke([
        SystemMessage(content="\n".join(system_parts)),
        *state["messages"],
    ])
    return {"messages": [response]}
L4 · LLM decides when to save memory

Noticesave_memoryThe docstring——This is a decision-making guide for LLMs, not for developers. After LLM reads the tool description, it independently determines whether there is information worth recording in the conversation. This is much smarter than writing rules to hard-code trigger times: it senses semantics rather than format.

L4 · Why save_memory only returns strings

The tool itself only does "recording intent" - the actualstore.put()The operation occurs insave_notesnode,Execute asynchronously after the respond node ends. This design keeps the respond node lightweight, and memory writing starts when the user gets the reply, without blocking the response speed at all.

Code block 4 / 5 Memory write node (dual mode) - L2 + L3 + L4
# ════════════════════════════════════════════════
# 【L2】update_profile 节点
#   · 用 trustcall 从完整对话历史提取 LearnerProfile
#   · 固定 key "user_profile":原地覆写,始终只有一份画像
#   · patch 模式:只更新对话中提及的字段
# ════════════════════════════════════════════════
async def update_profile(state: LearnerState, store: BaseStore):
    user_id    = state["user_id"]
    profile_ns = (user_id, "profile")

    # 先读取已有 Profile 作为上下文,让 trustcall 做增量更新
    existing = store.get(profile_ns, "user_profile")
    existing_profile = existing.value if existing else {}

    result = await profile_extractor.ainvoke({
        "messages": state["messages"],
        "existing": {"LearnerProfile": existing_profile},  # patch 模式的关键
    })

    if result["responses"]:
        updated = result["responses"][0].model_dump(exclude_none=True)
        # 【L1】写入 Store:固定 key,下次 put 覆写同一条记录
        store.put(profile_ns, "user_profile", updated)

# ════════════════════════════════════════════════
# 【L3 + L4】save_notes 节点
#   · 解析 respond 节点的工具调用结果
#   · 也用 notes_extractor 从对话中提取知识点
#   · UUID key:每条笔记独立追加,不覆盖历史
# ════════════════════════════════════════════════
async def save_notes(state: LearnerState, store: BaseStore):
    user_id  = state["user_id"]
    notes_ns = (user_id, "notes")

    # ── 方式 1:解析 LLM 工具调用(L4:Agent 主动触发)──────
    last_msg = state["messages"][-1]
    if hasattr(last_msg, "tool_calls") and last_msg.tool_calls:
        for tc in last_msg.tool_calls:
            if tc["name"] == "save_memory":
                args = tc["args"]
                note = {"topic": args["topic"], "content": args["content"],
                        "type": args.get("memory_type", "concept")}
                # 【L1 + L3】UUID key:每条笔记独立存储,追加不覆盖
                store.put(notes_ns, str(uuid.uuid4()), note)

    # ── 方式 2:无论 LLM 是否主动触发,也用 extractor 自动提取 ──
    result = await notes_extractor.ainvoke({"messages": state["messages"][-4:]})
    for note_obj in result["responses"]:
        store.put(notes_ns, str(uuid.uuid4()), note_obj.model_dump())
L2 · trustcall existing parameter is the key to patch

incoming"existing": {"LearnerProfile": old_profile}Tell trustcall "this is the current version". The patch mode of trustcall will compare the old and new ones.Generate only update instructions for changed fields (JSON Patch), rather than regenerating the entire Profile. This ensures that historical data will not be cleared due to partial information from a conversation.

L3 + L4 · Design intent of dual-channel writing

Method 1 (Tool call): Capture content that LLM considers "very important and should be proactively recorded".Method 2 (automatic extraction): Guaranteed strategy to ensure that even if LLM does not trigger a tool call, the knowledge points of this round of dialogue will not be lost. The two complement each other and the memory system is more reliable.

Code Blocks 5 / 5 Graph construction, dual Store mounting and multi-round running - L1 + L4
# ════════════════════════════════════════════════
# 【L1 + L4】图构建:双 Store 挂载
#   · checkpointer:对话内状态(断点/时间旅行)
#   · store:跨会话长期记忆(Profile + Notes)
# ════════════════════════════════════════════════
builder = StateGraph(LearnerState)
builder.add_node("retrieve_memories", retrieve_memories)
builder.add_node("respond",           respond)
builder.add_node("update_profile",    update_profile)   # 【L2】异步记忆节点
builder.add_node("save_notes",         save_notes)        # 【L3】异步记忆节点

builder.add_edge(START,                "retrieve_memories")
builder.add_edge("retrieve_memories",  "respond")
builder.add_edge("respond",            "update_profile")  # respond 后更新 Profile
builder.add_edge("respond",            "save_notes")      # respond 后保存笔记
builder.add_edge("update_profile",    END)
builder.add_edge("save_notes",         END)

# 【L1 + L4】同时挂载 checkpointer 和 store
graph = builder.compile(checkpointer=checkpointer, store=in_memory_store)

# ── 多轮对话示例 ──────────────────────────────────────────
user_id   = "learner_tony"
config_1  = {"configurable": {"thread_id": "session_001"}}

# ── 第一次会话:介绍自己 ──
result = graph.invoke({
    "user_id":   user_id,
    "messages": [HumanMessage(content="我叫 Tony,是个 Python 工程师,最近在学 LangGraph,对 Memory 机制特别感兴趣")]
}, config=config_1)
# → Profile 已更新: name=Tony, background=Python工程师, current_topics=[LangGraph]
# → Notes 新增: [concept] LangGraph/Memory 机制

# ── 第二次会话(新 thread,但 Store 记忆持久化)──
config_2 = {"configurable": {"thread_id": "session_002"}}
result2 = graph.invoke({
    "user_id":   user_id,
    "messages": [HumanMessage(content="InMemoryStore 和 Checkpointer 有什么区别?")]
}, config=config_2)
# retrieve_memories 自动搜索到:Tony 是 Python 工程师,正在学 LangGraph + Memory
# respond 节点用这些背景个性化回答,不再是"对陌生人的通用解释"
# → LLM 可能调用 save_memory 记录这个"question"类型的笔记

# ── 验证 Profile 持久化 ──
stored_profile = in_memory_store.get((user_id, "profile"), "user_profile")
print(stored_profile.value)
# {'name': 'Tony', 'background': 'Python工程师', 'current_topics': ['LangGraph'], ...}

# ── 验证 Notes 积累 ──
all_notes = in_memory_store.search((user_id, "notes"), query="LangGraph memory")
for note in all_notes:
    print(f"[{note.value['type']}] {note.value['topic']}: {note.value['content']}")
# [concept] LangGraph: Memory 机制 - Checkpointer vs Store 的区别
# [question] LangGraph: InMemoryStore 与 Checkpointer 的具体区分
L4 · thread_id has changed but the memory is still there

The second session used a new one.thread_id: "session_002", there is no news about the first conversation in checkpointer - but there is Tony's Profile and notes in Store.This is the core value of Store: cross-thread persistence.When the user closes the app and opens it again, the AI still remembers who he is and what he is learning.

L4 · The meaning of two add_edge("respond", ...)

respond → update_profileandrespond → save_notesTwo edges make two memory nodesParallel execution(LangGraph’s fan-out semantics). After the user sees the reply, Profile updates and note saving occur simultaneously, which is twice as fast as serial. This is the embodiment of L4's "asynchronous writing" design in the graph structure.

Advanced design points for this example
Profile + Collection dual memory coexistence

Maintain user portraits (single overwrite) and study note collections (append one by one) at the same time. The two memories serve different purposes and do not conflict. Real personalized assistants usually require the cooperation of two modes.

Dual-way memory writing (tool + automatic extraction)

The "accurate recording" of LLM's active calling tool and the "backup guarantee" of automatic extraction by trustcall are two-pronged, so that no memory is missed and the coverage rate is higher.

The type field supports classification retrieval

LearningNote's type field (concept/question/insight) allows you to filter and retrieve only "question type notes that the user has not answered" in the future to achieve more accurate memory recall.

trustcall patch mode incremental update

Profile update adopts patch mode, which only modifies the fields involved in this round of dialogue, and historical information is safely retained. Avoid the risk of data loss caused by "regenerating the entire Profile for each update".

Return to Module 5 Home Page