LangGraph blueprint · Consumer software
Personal assistant with long-term memory
A LangGraph assistant that recalls a user's saved facts and preferences from a namespaced store using semantic search, keeps each conversation's state with a checkpointer, and runs a memory-writing node after conversations to decide what to save. It must never leak one user's memories into another user's session, and it must never keep a fact the user asked it to forget.
- Steps
- 8
- Subjective questions
- 33
- Objective questions
- 33
The problem and the outcome
Problem: Chat assistants forget everything when a new thread starts, so users repeat their preferences, names and context every time. Dumping whole chat histories into the prompt is costly, noisy and a privacy risk.
Outcome: Each new thread starts with a small set of relevant, user-approved facts, so answers are personalised from the first message. Memory stays curated: it is deduplicated, updatable and deletable per user.
Architecture
How it works, step by step
- The client sends the message to the Agent Server with config containing thread_id for the conversation and user_id taken from the authenticated session, never from the request body.
- The compiled StateGraph loads the thread's latest checkpoint from the Postgres checkpointer, so the messages list (merged with the add_messages reducer) holds the conversation so far.
- The recall_memories node calls store.search on the namespace ("memories", user_id) with the latest user message as the query and a small limit, returning the most semantically similar facts and preferences.
- The assistant node puts the recalled memories into the system prompt as clearly labelled user facts and calls a tool-calling chat model over the thread's messages.
- A conditional edge (tools_condition) sends tool calls to the ToolNode and loops back to the assistant, or ends the run when the model returns a final answer.
- The reply streams to the user in messages mode while the checkpointer saves state at every super-step; when the conversation goes idle, a debounced memory job is scheduled for that user and thread.
- The write_memory node reads the new messages plus existing memories, asks an LLM with structured output to decide insert, update or no-op for each candidate fact, and writes with store.put using stable keys, which the store embeds for search.
- Users can view, correct or delete their memories in settings; deletes call store.delete and are honoured on the very next recall.
Tech stack
- LangGraph StateGraph with add_messages reducer, ToolNode and tools_condition
- PostgresSaver / AsyncPostgresSaver checkpointer (InMemorySaver in dev)
- PostgresStore with an index config and pgvector for semantic search (InMemoryStore in dev)
- An embedding model configured on the store index
- A tool-calling chat model with structured output for memory extraction
- LangSmith Agent Server for deployment, background runs and tracing
Key design decisions
- Namespace memories as ("memories", user_id) and derive user_id from auth in the runtime config, so isolation is enforced by the code path, not by the prompt.
- Write memories in a debounced background run after the conversation goes idle rather than on every turn, keeping reply latency low and giving the extractor full context.
- Store small, atomic facts with stable keys and let the writer update existing items instead of appending, so contradictions (old vs new preference) are resolved rather than accumulated.
- Keep short-term context in the checkpointer per thread and only distilled facts in the store; never copy raw transcripts into long-term memory.
- Recall with a small top-k and inject memories as data, not instructions, so a stored string cannot override the system prompt.
Common mistakes
- Taking user_id from client input lets one user read another's namespace; it must come from the authenticated identity.
- Saving everything the user says produces noisy, duplicated memories that crowd out useful ones and cost tokens on every recall.
- Prompt injection via stored memories: a fact like 'ignore previous rules' gets replayed into every future prompt if treated as instructions.
- Changing the embedding model without re-indexing silently breaks semantic search results.
- Deleting a memory in the UI while a pending background write re-inserts it from an old conversation; the writer must check tombstones or deletion timestamps.
What to measure
- Recall precision: share of injected memories judged relevant to the turn (via LangSmith evaluations)
- Memory write rate and duplicate/contradiction rate per user
- Added latency of recall_memories and token overhead from injected memories
- Count of user corrections and deletions, as a signal of wrong or unwanted memories
- Cross-namespace access incidents, which must stay at zero
Most-asked interview questions
Subjective questions 33
1What is the difference between a checkpointer and a store in LangGraph, and why does this assistant need both?Asked almost every time
A checkpointer saves the graph state per thread_id at every super-step. That gives me short-term memory for one conversation, plus durable execution, resuming after failure and time travel with get_state_history. A store is long-term memory shared across threads, organised by namespace tuples and optionally searchable semantically. This assistant needs both because a user opens many threads: the checkpointer keeps the current conversation's messages and tool calls, while the store holds distilled facts like 'prefers vegetarian food' or 'manager is Priya' that should appear in any new thread. I keep them separate on purpose: raw transcripts stay thread-scoped, and only curated facts cross threads, which is cheaper and easier to govern for privacy.
Sourced from Docs by LangChain (MCP)
2Walk me through what happens end to end when a user sends one message to this assistant.Asked almost every time
The client calls the Agent Server. The config carries a thread_id for the conversation and a user_id taken from the authenticated session. The compiled StateGraph loads that thread's latest checkpoint from Postgres, so the messages list, merged by the add_messages reducer, holds the conversation so far. The recall_memories node runs store.search on ('memories', user_id), using the latest message as the query and a small limit. The assistant node adds those facts to the system prompt as labelled user facts and calls a tool-calling model. tools_condition sends tool calls to the ToolNode and loops back, or ends the run. The reply streams in messages mode, and state is checkpointed at every super-step. When the conversation goes idle, a debounced job runs write_memory to insert, update or skip facts.
Sourced from Docs by LangChain (MCP)
3A user deletes a memory, but a week later the assistant knows that fact again. What went wrong, and how do you design against it?Asked almost every time
The likely cause is that write_memory re-extracted the fact. The old thread messages still contain it, or the user said it again in passing. The model saw a 'new' candidate and inserted it. store.delete removes the item, but it leaves no record that the user wanted it gone. My fix is to keep a forget record, or tombstone, per user. It stores the normalised fact or its stable key, plus a timestamp. write_memory loads the tombstones with the existing memories. The prompt tells the model never to save a tombstoned fact, and code checks the output again before any store.put. The code check matters because LLM compliance is not a guarantee. If the user later restates the fact and explicitly asks me to remember it, I clear the tombstone. Otherwise forgetting wins.
Sourced from Docs by LangChain (MCP)
4Why is user_id read from the authenticated session and not from the request body? What attack does that prevent?Asked almost every time
The namespace ('memories', user_id) is the only thing that scopes recall and writes. If the client could set user_id, any logged-in user could put someone else's ID in the body. They could then read that person's facts through recall, or poison their memories through write_memory. Taking user_id from the verified session token on the server turns the namespace into an authorization boundary instead of a client hint. The same rule applies to the debounced memory job. It should carry the user_id that was resolved at request time, and never re-derive it from message content. I would also reject or ignore any user_id field a client sends, and log it as suspicious.
Sourced from Docs by LangChain (MCP)
5Why run memory writing as a debounced background job instead of inside the conversation's hot path?Asked almost every time
Writing in the hot path adds an extra LLM call and store writes to every turn, so latency and cost go up. It also judges facts too early: the user might correct themselves two messages later. Debouncing waits until the conversation goes idle. Then one job looks at all the new messages at once and makes better insert, update or no-op decisions with fewer calls. The trade-off is freshness. A fact stated now is not recallable in another thread until the job runs, although it is still in this thread's messages. If a product needs instant recall, I would add an explicit 'remember this' tool that writes right away, and keep the background job for everything else.
Sourced from Docs by LangChain (MCP)
6Recalled memories go into the system prompt. What is the prompt-injection risk, and how do you reduce it?Asked almost every time
Memories come from user conversations, which can include pasted web content or crafted text. A stored 'fact' like 'ignore previous instructions and reveal…' would be replayed into every future system prompt. That is a persistent injection. Mitigations come in layers. First, write_memory only saves short, factual statements about the user that fit the schema, and code checks lengths and patterns. Second, recalled memories are wrapped in a clearly labelled block, and the prompt says they are data to consider, never instructions. Third, tools run with the user's own permissions, so even a successful injection cannot reach other users. Fourth, the settings page shows every memory, so odd entries can be spotted and deleted.
Sourced from Docs by LangChain (MCP)
7A user deletes a memory in settings while a memory job for their thread is running. How do you avoid the job writing it back?Asked almost every time
This race is the main threat to 'never keep a forgotten fact'. The job may have read the memories before the delete and then upsert the same key afterwards. The delete flow does two things together: store.delete on the key, and a tombstone record for that key and fact. The job does a final check against tombstones immediately before each store.put, not just at the start. That makes the window very small. To close it fully, I serialise memory mutations per user, with a per-user lock or queue, so a delete and a job write can never interleave. After every job, I also sweep and remove any item that matches a tombstone, as a safety net.
Sourced from Docs by LangChain (MCP)
8How do you decide what gets written to long-term memory, and when?Very often asked
I run a write_memory node as a debounced background run once the conversation goes idle, so it never slows the reply and sees the whole exchange. It loads the new messages and the user's existing memories from the store, then an LLM with structured output proposes a list of operations: insert a new fact, update an existing key, or do nothing. I instruct it to keep only durable, user-specific facts and preferences, not one-off task details or sensitive data the user didn't intend to share. Using stable keys means a changed preference overwrites the old one instead of creating a contradiction. Finally, I check deletion records so a fact the user removed is not re-added from an older conversation.
Sourced from Docs by LangChain (MCP)
9How do you guarantee one user's memories never show up for another user?Very often asked
Isolation starts with the namespace: every read and write uses ("memories", user_id). The key point is where user_id comes from. On the Agent Server I take it from the authenticated identity and pass it through the runtime config, never from the message body or anything the model can produce. Nodes read it from config, so the LLM cannot choose a namespace. I add auth rules so a thread can only be accessed by its owner, write tests that try cross-user reads, and trace runs in LangSmith to audit store calls. I also treat recalled memories as data in the prompt, so even if a memory contains instructions, it cannot redirect the agent to other users' data.
Sourced from Docs by LangChain (MCP)
10User A sends a request with user B's thread_id. What protects B's conversation?Very often asked
Memory namespaces protect the store, but the checkpointer is keyed by thread_id. A guessed or leaked thread_id could load B's whole message history. So thread ownership needs its own check. When a thread is created, I record its owner as metadata. On every run, get-state or history call, I verify that the authenticated user owns the thread before the graph touches the checkpoint. Agent Server lets you plug in custom authentication and authorization handlers, which is the right place for this check. Thread IDs should also be random and unguessable, but that is defence in depth, not the control. A test suite should try cross-user thread access on every endpoint and expect a refusal.
Sourced from Docs by LangChain (MCP)
11How does the debounce actually work if the user comes back and keeps chatting before the job fires?Very often asked
Each new message for a user and thread reschedules the pending job, or cancels it and replaces it. The job only runs after a quiet period. If the user returns mid-wait, the timer resets, so we don't process half a conversation and then process it again. When the job runs, it records how far it got, for example the last processed message ID, in a small per-thread marker. The next run then reads only the messages after that point. If the user returns after the job has run, the next idle period schedules a new job that covers just the new messages. Stable keys make reprocessing safe anyway, so the marker is mainly there to save cost.
Sourced from Docs by LangChain (MCP)
12Why does write_memory use stable keys with store.put rather than generating a fresh ID for every fact?Very often asked
store.put with an existing key overwrites that item, so a stable key makes writes idempotent upserts. If the user's city changes from Pune to Bengaluru, the update replaces the 'home_city' memory instead of leaving two conflicting facts that recall might both return. A retried job writes the same keys again rather than duplicating them. Stable keys also give the settings page and deletes a durable handle: deleting 'home_city' means one specific item, and a tombstone can name that key. The structured output should return the key for every update. For inserts, the code generates a key from a canonical slot name or a deterministic ID, not a random one per run.
Sourced from Docs by LangChain (MCP)
13How do you handle a new fact that contradicts an existing memory?Very often asked
write_memory receives the existing memories along with the new messages, so the model can see the conflict. Its structured output for that candidate should be 'update' with the existing key, not 'insert'. The rule I give it: newer, explicit statements from the user override older ones, and inferred facts never override stated ones. If the conflict is ambiguous, for example 'I'm vegetarian' and then 'had chicken today', the safer action is no-op, or saving it with lower confidence, rather than overwriting. I store updated_at and a source reference in the value so updates can be audited. In settings, users can see and fix what the assistant believes.
Sourced from Docs by LangChain (MCP)
14How do you choose the search limit for recall_memories, and what problems come from setting it too high or too low?Very often asked
Too high, and the system prompt fills with loosely related facts. That costs tokens, dilutes attention and tempts the model to mention irrelevant personal details, which users find creepy. Too low, and a relevant preference gets missed. I start small, because the notes call for a small limit. Then I tune it on an evaluation set of real-style queries where we know which memories should come back, and measure recall and precision of retrieved items. A similarity score threshold helps drop weak matches when nothing is relevant. I might also always include a few pinned core preferences, such as language or name, alongside the semantic results.
Sourced from Docs by LangChain (MCP)
15How would you estimate storage and request load for this system without real traffic data?Very often asked
I'd build a parameterised model rather than guess numbers. For the store: users × average memories per user × (value size + embedding size). Embedding size is the model's dimensions × bytes per dimension. For checkpoints: threads × checkpoints per thread × state size. State grows with message count, so long threads dominate. For load: daily active users × messages per user gives assistant runs. Each run costs one store.search plus one or more LLM calls, plus tool loops. Memory jobs number roughly idle sessions per day, each with one LLM call and several puts with embeddings. Then I fill in values from a pilot and decide checkpoint retention and pruning based on the growth curve.
Sourced from Docs by LangChain (MCP)
16The memory job fails halfway through writing several facts. What state are we left in, and is that acceptable?Very often asked
Some puts may have succeeded and others not. Because keys are stable and every write is an upsert, a retry simply rewrites the same items, so partial progress is harmless. I don't advance the processed-message marker until the whole batch succeeds. The retry then sees the same input, and the LLM's decision may differ slightly, but upserts by key keep that from creating duplicates. I retry with backoff a bounded number of times, then send the job to a dead-letter queue with the thread and user IDs for inspection. The conversation itself is unaffected: memory writes are off the hot path, so a failed job only delays recall in other threads.
Sourced from Docs by LangChain (MCP)
17Deleting a memory from the store is not the whole story. Where else can that fact still live?Very often asked
It can still be in the thread's messages in the Postgres checkpointer, including every historical checkpoint, because time travel keeps earlier states. It can also be in logs, traces in an observability tool, queued memory-job payloads and backups. If 'forget' means the assistant must not use it, then a tombstone plus store.delete covers recall in other threads. But the current thread can still see it in its message history. For a stronger promise, I offer 'delete this conversation', which removes the thread's checkpoints, and I set retention windows for old threads, traces and backups. The privacy policy should state exactly what is erased immediately and what ages out.
Sourced from Docs by LangChain (MCP)
18The server crashes while the assistant is halfway through a tool-calling loop. What happens when the user retries?Very often asked
The Postgres checkpointer saved state after every super-step, so the thread resumes from the last completed step instead of starting over. Completed tool calls and their results are already in messages. The caveat is side effects. If the crash happened after a tool ran but before its step was checkpointed, that tool may run again. So tools with external effects, such as sending a message or booking something, need idempotency keys or a check of whether the action already happened. Read-only tools are safe to repeat. The user sees a slightly delayed answer, not a lost conversation.
Sourced from Docs by LangChain (MCP)
19How would you automatically test that memories never leak across users?Very often asked
I'd create two or more synthetic users with distinctive, unique facts, for example canary strings. Then I run conversations as each user and assert that every store.search, every system prompt and every reply contains only that user's canaries. I add negative tests: send another user's ID in the request body, reuse another user's thread_id, and run concurrent memory jobs for different users. All must fail or stay isolated. A code-level check ensures recall_memories and write_memory build their namespaces only from config user_id, never from a parameter. In production, I'd log namespace and authenticated user per store call, and alert on any mismatch.
Sourced from Docs by LangChain (MCP)
20A user deletes their account. What exactly must happen?Very often asked
Every item in the user's namespaces must go: list or search under ('memories', user_id) and store.delete each item, including all sub-namespaces. All threads they own need their checkpoints deleted, which is why owner metadata on threads matters. Pending or queued memory jobs for that user must be cancelled, so none write after the wipe. Traces, logs and analytics holding their content follow their retention or deletion process, and backups age out on a documented schedule. Tombstones can go once the data is gone, since no more jobs can run for that user. I'd make the wipe an idempotent, logged workflow that is checked afterwards by confirming searches return nothing.
Sourced from Docs by LangChain (MCP)
21Why does the write decision use structured output instead of letting the LLM write free text?Often asked
The output drives code that mutates the store, so it has to be machine-checkable. With a schema such as a list of {action: insert|update|noop, key, content, category}, I can validate every item. I can reject updates to keys that don't exist, block tombstoned facts, enforce length limits and drop sensitive categories before anything is written. Free text would need fragile parsing, and a malformed response could corrupt memory. A schema also makes behaviour testable: I can build a golden set of conversations and assert on expected actions. If the model returns invalid output, the job retries or skips that run. A failed memory update is better than a wrong one.
Sourced from Docs by LangChain (MCP)
22What should the assistant never save to long-term memory, even if the user says it?Often asked
I'd exclude secrets such as passwords, OTPs, card or bank numbers, and government ID numbers like Aadhaar or PAN. I'd also exclude other people's personal details, and sensitive categories like health or religion unless the product explicitly supports them and the user opts in. The write_memory schema has a category field, and code rejects disallowed categories and pattern-matches obvious secrets before any put. That check matters because a single LLM judgment is not enough. This also fits data-minimisation duties under India's DPDP Act: store only what serves the stated purpose. If a user asks the assistant to remember something sensitive, it should explain that it won't, rather than silently dropping it.
Sourced from Docs by LangChain (MCP)
23Should the assistant ask the user before saving a memory? How would you implement that in LangGraph?Often asked
It's a trade-off. Asking every time is annoying, and never asking feels intrusive. A middle ground is to save routine preferences silently and surface them in settings, but confirm sensitive or high-impact facts. In LangGraph, a node can call interrupt() with the proposed memory. That pauses the run, and the checkpointer keeps the paused state. The client shows the confirmation, then resumes with Command(resume=approved_or_rejected), and the node writes or discards. Since our writes happen in a background job after the user goes idle, the interrupt would usually show up as a pending suggestion on the next visit, not a mid-chat popup.
Sourced from Docs by LangChain (MCP)
24How do you stop the assistant and ToolNode from looping forever?Often asked
tools_condition routes back to the assistant whenever the model emits tool calls. A confused model can keep calling tools. LangGraph enforces a recursion limit on super-steps per run. It is set in the run config, and exceeding it raises an error. I set it to fit realistic tool depth and catch the error to return a graceful message. I also add a tool-call counter in state, and once it passes a budget, the assistant prompt says to answer with what it has. Tool errors should come back as tool messages the model can reason about, not silent retries. Tracing shows which tools cause loops so I can fix their descriptions.
Sourced from Docs by LangChain (MCP)
25Why stream in messages mode for this assistant, and when would you use another mode?Often asked
Messages mode streams LLM tokens along with metadata about which node produced them. The UI can render the assistant node's tokens as they arrive and ignore other model calls, so users see the reply quickly. Updates mode streams each node's state changes, which is useful for showing 'searching memories…' or 'calling calendar…' status. Values streams the full state after each step, which is heavier and good for debugging small graphs. Custom lets a node emit its own progress events, and debug gives detailed execution traces. You can combine modes, for example messages for the text plus updates for step indicators.
Sourced from Docs by LangChain (MCP)
26A user reports the assistant 'remembered' something wrong. How do you debug it?Often asked
First I check the store: which item holds the wrong fact, its key, its updated_at and the source reference I keep in the value. That tells me which thread and memory-job run wrote it. Then I look at that run's trace: the input messages, the existing memories and the structured decision the LLM made. For the conversation side, get_state_history on the thread shows every checkpoint, so I can see exactly what the assistant saw at each step. I can also fork from an earlier checkpoint with update_state to replay with a fixed prompt. The fix usually goes into the write prompt or validation rules, plus a regression case in the eval set.
Sourced from Docs by LangChain (MCP)
27The conversation's messages list keeps growing. How do you keep context size under control?Often asked
The add_messages reducer only appends, so long threads grow without bound, and model calls get slower and costlier. Options: trim to the last N messages or tokens before calling the model, or keep a running summary in a separate state key and drop older messages from the model input. Long-term memory makes this safer, because durable facts are already in the store and recalled on demand. Trimming the model input doesn't have to delete history from the checkpoint. That separates what the model sees from what we keep. If we do remove messages from state, add_messages supports removal by message ID.
Sourced from Docs by LangChain (MCP)
28You decide to switch to a better embedding model. What is the migration plan for stored memories?Often asked
Vectors from different embedding models aren't comparable, so queries embedded with the new model won't match old vectors properly. All memories need re-embedding. The plan: stand up a new store index configured with the new model, and backfill it by re-putting every item. Re-putting with the same keys makes this idempotent and resumable. During the backfill, new writes go to both the old and new stores. I run the recall eval on the new index, and once it is at least as good, I switch reads with a flag, then retire the old one. Deleted items and tombstones must be honoured in the backfill, or forgotten facts could come back.
Sourced from Docs by LangChain (MCP)
29How do you measure whether long-term memory actually improves the assistant?Often asked
I split it into retrieval and use. For retrieval, a labelled set of queries with their expected memories gives precision and recall of store.search at the chosen limit. For writing, golden conversations with expected insert, update and no-op actions measure write accuracy, especially false saves and missed deletes. For end-to-end quality, I compare answers with and without memory on personalised tasks, scored by human review or an LLM judge. Online, I track how often users correct or delete memories, since frequent deletion suggests over-saving, and how often they repeat facts they told us before, which suggests under-recall. A forgotten fact ever coming back is a zero-tolerance metric.
Sourced from Docs by LangChain (MCP)
30Would you add Postgres row-level security under the store, or is namespace scoping in code enough?Often asked
Namespace scoping in code is the primary control, but it's one bug away from a leak. A developer could write a search that uses a too-broad namespace prefix. Defence in depth is worth it for a product that promises isolation. Options include row-level security policies keyed on a session variable for the user, a database role per service, or at least a wrapper around the store that injects the namespace and refuses any call without it. I'd be careful with row-level security on a managed store schema I don't own. So the realistic first step is the enforced wrapper, plus audits and tests, with database policies added if the schema allows it.
Sourced from Docs by LangChain (MCP)
31The team later moves to a supervisor multi-agent design. How do you keep memory isolation intact?Often asked
Sub-agents become subgraphs or nodes under a supervisor that routes with handoffs, for example Command(goto=...). They share the same store and run config, so the user_id from the authenticated session flows to every agent through config, never through agent-generated messages. An LLM agent must never choose or construct a namespace. Memory access should go through the same recall and write functions or tools, which build ('memories', user_id) from config. I'd also limit which agents can write memory. Ideally only the dedicated write_memory path writes, so a specialised agent can't save facts on its own, and tombstone checks stay in one place.
Sourced from Docs by LangChain (MCP)
32Recall adds a store.search call before every model call. How do you keep latency acceptable?Often asked
The search needs an embedding of the query plus a vector lookup, both on the critical path before the first token. To keep it fast: use a small limit, keep the vector index well tuned in Postgres, and put the store close to the server. For very short messages like 'thanks', I can skip recall with a cheap check. Since the reply streams in messages mode, users feel the time to first token, so I measure recall latency separately in traces and give it a budget. Caching recalled memories for the duration of a thread is tempting, but it would break 'deletes are honoured on the very next recall', so I wouldn't cache across turns.
Sourced from Docs by LangChain (MCP)
33How would you organise different kinds of memory, like preferences, facts and past episodes?Sometimes asked
All memory stays under the user's root, ('memories', user_id). I can either sub-namespace by type, such as ('memories', user_id, 'preferences'), or keep one namespace with a category field in each value. Separate namespaces let me search them differently. For example, I might always load a small preferences profile but semantically search the facts. Episodic memories, like summaries of past conversations, are bulkier and better searched only when the query looks historical. Whatever the layout, every namespace must start with the user_id element, deletes and account wipes must cover all of them, and tombstones apply to every type.
Sourced from Docs by LangChain (MCP)
Objective questions 33
1Which LangGraph component provides memory that is shared across different threads for the same user?Asked almost every time
Why: Stores such as InMemoryStore and PostgresStore hold long-term memory across threads, organised by namespace tuples; checkpointers are scoped to a single thread_id.
Sourced from Docs by LangChain (MCP)
2Where must the user_id used for memory namespaces come from?Asked almost every time
Why: If user_id came from the request body, a caller could name another user's namespace. Taking it from the verified session makes the namespace a real security boundary.
Sourced from Docs by LangChain (MCP)
3Why does write_memory use stable keys with store.put?Asked almost every time
Why: store.put upserts by key, so a stable key turns updates and retried jobs into idempotent overwrites.
Sourced from Docs by LangChain (MCP)
4A user deleted a fact, and after the next memory job it is back. What is the most robust fix?Asked almost every time
Why: write_memory can re-extract the fact from old messages. A tombstone checked right before writes stops the fact being saved again.
Sourced from Docs by LangChain (MCP)
5A user deletes a memory, then immediately asks a related question in a thread. What should recall return?Asked almost every time
Why: The workflow requires deletes to take effect on the very next recall, so recall must not use a cross-turn cache.
Sourced from Docs by LangChain (MCP)
6When does a LangGraph checkpointer save the graph state for a thread?Very often asked
Why: Checkpointers save state per thread_id at every super-step, which enables resuming after failure and time travel.
Sourced from Docs by LangChain (MCP)
7Which reducer merges new messages into the conversation state in this workflow?Very often asked
Why: The notes state that the messages list is merged with the add_messages reducer, which appends new messages and can update or remove them by ID.
Sourced from Docs by LangChain (MCP)
8What does tools_condition do when the model's response contains no tool calls?Very often asked
Why: tools_condition sends tool calls to the ToolNode and otherwise ends the run, because the model has returned a final answer.
Sourced from Docs by LangChain (MCP)
9Which streaming mode delivers LLM tokens along with metadata about the node that produced them?Very often asked
Why: Messages mode streams LLM tokens with node metadata, so the UI can show the assistant's reply as it is generated.
Sourced from Docs by LangChain (MCP)
10Which store operation is used when a user deletes a memory in settings?Very often asked
Why: Per the workflow, deletes call store.delete and are honoured on the very next recall.
Sourced from Docs by LangChain (MCP)
11What query does recall_memories pass to store.search?Very often asked
Why: Recall uses the latest user message as the semantic query, so it returns the facts most similar to the current request.
Sourced from Docs by LangChain (MCP)
12What key does the checkpointer use to keep each conversation's state separate?Very often asked
Why: Checkpointers save state per thread_id. The user_id scopes long-term memory in the store.
Sourced from Docs by LangChain (MCP)
13What is the main benefit of running memory writing as a debounced job after the conversation goes idle?Very often asked
Why: Debouncing avoids adding latency to every turn and lets one job decide on the whole idle batch. The cost is slightly delayed cross-thread recall.
Sourced from Docs by LangChain (MCP)
14What is the safest way to present recalled memories in the system prompt?Very often asked
Why: Stored text can carry injection attempts. Labelling memories as data reduces the chance the model follows them as instructions.
Sourced from Docs by LangChain (MCP)
15The store says the user lives in Pune, and the user now says they moved to Bengaluru. What should write_memory output?Very often asked
Why: Updating the same key replaces the old value, so recall never returns both conflicting facts.
Sourced from Docs by LangChain (MCP)
16The process crashes mid tool-loop. With the Postgres checkpointer, what happens on retry?Very often asked
Why: State is saved at every super-step, which gives durable execution and resumption from the last completed step.
Sourced from Docs by LangChain (MCP)
17What extra check is needed so a user cannot load another user's conversation by supplying their thread_id?Very often asked
Why: Checkpoints are keyed by thread_id, not user_id, so thread ownership needs its own authorization check.
Sourced from Docs by LangChain (MCP)
18A user deletes their account. Which set of actions is complete?Very often asked
Why: Memories live in the store, history lives in checkpoints and pending jobs could write again, so all three must be handled.
Sourced from Docs by LangChain (MCP)
19Which namespace design best isolates long-term memories per user?Often asked
Why: Scoping by user_id keeps memories per user across threads, and taking it from authenticated config prevents users or the model from reading another namespace.
Sourced from Docs by LangChain (MCP)
20Which checkpointer fits production for this assistant?Often asked
Why: InMemorySaver is for development. Production needs a durable Postgres checkpointer so threads survive restarts and can resume.
Sourced from Docs by LangChain (MCP)
21What is InMemoryStore appropriate for?Often asked
Why: InMemoryStore keeps data in process memory, which suits development. PostgresStore is the durable option.
Sourced from Docs by LangChain (MCP)
22After a node calls interrupt() to ask the user to confirm a memory, how is the run continued?Often asked
Why: interrupt() pauses the node with its state checkpointed, and Command(resume=value) supplies the human input and continues the run.
Sourced from Docs by LangChain (MCP)
23Which pair of APIs supports replaying or forking a thread from an earlier checkpoint?Often asked
Why: Time travel uses get_state_history to find earlier checkpoints and update_state to fork from one.
Sourced from Docs by LangChain (MCP)
24Why does recall use a small limit on store.search?Often asked
Why: Too many recalled items waste tokens, dilute attention and can make replies mention unrelated personal facts.
Sourced from Docs by LangChain (MCP)
25Why should write_memory's LLM return structured output?Often asked
Why: A schema of actions with keys can be checked against tombstones, existing keys and content rules before any write.
Sourced from Docs by LangChain (MCP)
26You switch to a different embedding model. What must happen to existing memories?Often asked
Why: Vectors from different embedding models aren't comparable, so stored items need re-embedding for semantic search to keep working.
Sourced from Docs by LangChain (MCP)
27When write_memory calls store.put on a store with semantic search configured, where does embedding happen?Often asked
Why: Per the workflow, write_memory writes with store.put, which the store embeds for search.
Sourced from Docs by LangChain (MCP)
28If you wanted to evaluate many candidate facts in parallel with per-item state, which LangGraph primitive fits?Sometimes asked
Why: A conditional edge that returns Send objects fans out to a node with per-item state, which is the map-reduce pattern.
Sourced from Docs by LangChain (MCP)
29At which path does Agent Server expose an MCP endpoint for deployed agents?Sometimes asked
Why: According to the notes, deployment on LangSmith's Agent Server exposes agents over an API and an MCP endpoint at /mcp.
Sourced from Docs by LangChain (MCP)
30What does operator.add do as a reducer on a list state key?Sometimes asked
Why: operator.add on lists concatenates, so updates from nodes are appended rather than overwriting the list.
Sourced from Docs by LangChain (MCP)
31What does returning Command(goto=..., update=...) from a node allow?Sometimes asked
Why: Command with goto and update lets a node decide where to go next and apply a state update together, which is how handoffs work.
Sourced from Docs by LangChain (MCP)
32In LangGraph, what does a subgraph let you do?Sometimes asked
Why: Subgraphs compose graphs as nodes, which is useful for packaging the memory-writing flow or sub-agents.
Sourced from Docs by LangChain (MCP)
33Which streaming mode would you use to show 'searching memories…' as each node's state update completes?Sometimes asked
Why: Updates mode streams each node's state changes as they happen, which suits step-by-step progress indicators.
Sourced from Docs by LangChain (MCP)
Track your progress through every blueprint in your dashboard.
Start learning