Agent Memory 的本质写入、检索、注入Agent 不是天然会记住你。所谓 Memory本质就是三件事1. 写入策略什么信息值得保存 2. 检索策略当前问题需要哪些记忆 3. 注入策略怎么把记忆塞给模型我们用一个本地json_memory.json版本把这三件事看清楚。1. 整体流程User: I am DenialLangGraph APIretrieve_memoryassistantwrite_memoryjson_memory.json对应代码builder.add_node(retrieve_memory,retrieve_memory)builder.add_node(assistant,call_model)builder.add_node(write_memory,write_memory)builder.add_edge(START,retrieve_memory)builder.add_edge(retrieve_memory,assistant)builder.add_edge(assistant,write_memory)builder.add_edge(write_memory,END)流程很简单先读记忆 - 再问模型 - 最后写记忆2. State 是运行时账本LangGraph 的核心是维护State。classState(MessagesState):current_user_text:strassistant_text:strmemories:list[dict[str,Any]]saved_memories:list[dict[str,Any]]几个关键字段字段作用messages对话消息current_user_text本轮用户输入memories检索出来的历史记忆assistant_text模型回复saved_memories本轮新保存的记忆一条消息的流转messages: I am Denialretrieve_memorycurrent_user_text I am Denialmemories matched memoriesassistantassistant_text replywrite_memorysaved_memoriesjson_memory.json3. 写入策略什么值得记不是每句话都要保存。我们用关键字判断def_should_save_user_fact(text:str)-bool:loweredtext.lower()patterns[remember,my name is,i am,i like,i dislike,i prefer,my goal is,]returnany(patterninloweredforpatterninpatterns)例子I am Denial转成小写后i am Denial命中i am所以保存。写入 JSON{text:I am Denial,source:json_memory_graph,assistant_response:Nice to meet you, Denial!}对应节点defwrite_memory(state:State):current_textstate.get(current_user_text)ifnotcurrent_textornot_should_save_user_fact(current_text):return{saved_memories:[]}memory{text:current_text.strip(),assistant_response:state.get(assistant_text),}memories_read_memories()memories.append(memory)_write_memories(memories)return{saved_memories:[memory]}一句话写入策略 判断这句话有没有长期价值。4. 检索策略现在该想起什么用户问问题时不是把所有记忆都塞给模型而是先找相关记忆。我们的简化版用“词交集”打分def_score_memory(query:str,memory:dict[str,Any])-int:query_terms_terms(query)memory_textstr(memory.get(text,))memory_terms_terms(memory_text)scorelen(query_termsmemory_terms)ifmemory_textandmemory_textinquery:score2returnscore关键是这一句query_termsmemory_terms它表示两个集合的交集。例子query: What is my goal? memory: My goal is to earn 50k per month拆词query_terms {what, is, my, goal} memory_terms {my, goal, is, to, earn, 50k, per, month}交集{my, goal, is}分数score 3检索节点defretrieve_memory(state:State):current_message_latest_human_message(state)query_message_content(current_message)ifcurrent_messageelseall_memories_read_memories()rankedsorted(all_memories,keylambdamemory:_score_memory(query,memory),reverseTrue,)relevant[mforminrankedif_score_memory(query,m)0]ifnotrelevant:relevantall_memories[-5:]return{memories:relevant[:5],current_user_text:query,}一句话检索策略 从历史记忆里找当前最相关的几条。5. 注入策略怎么让模型知道模型不会自己读 JSON。所以要把检索出的记忆写进 prompt。defcall_model(state:State):current_textstate.get(current_user_text)memoriesstate.get(memories,[])memory_context\n.join(f-{memory[text]}formemoryinmemories)system_messageSystemMessage(content(Use the long-term memories only when relevant.\n\nfLong-term memories:\n{memory_context}))responsellm.invoke([system_message,HumanMessage(contentcurrent_text),])return{messages:[response],assistant_text:response.content,}如果 JSON 里有I am Denial用户问What is my name?实际给模型的是System: Long-term memories: - I am Denial Human: What is my name?一句话注入策略 把检索出来的记忆放进模型上下文。6. 一次完整运行用户输入I am DenialLangGraph API 接收到{input:{messages:[{role:user,content:I am Denial}]}}节点流转retrieve_memory 读 messages 得到 current_user_text I am Denial 返回 memories assistant 读取 current_user_text 读取 memories 调用 LLM 返回 assistant_text write_memory 读取 current_user_text 判断是否命中 i am 写入 json_memory.json 返回 saved_memories最终json_memory.json 里多了一条 I am Denial7. 一个关键工程经验一开始我们让write_memory自己从messages里找用户输入。后来发现不稳定。更稳的做法是retrieve_memory 负责提取用户输入 显式写入 state.current_user_text 后续节点统一读 current_user_text也就是关键中间结果不要让后续节点重复猜。 显式放进 State。总结Agent Memory 可以拆成三个问题1. 什么值得记 - 写入策略 2. 现在该想起什么 - 检索策略 3. 怎么告诉模型 - 注入策略LangGraph 做的事是维护 State 执行节点 合并节点返回 控制流程流转所以调试 Agent 时只盯住三句话这个节点读了哪些 State 这个节点返回了哪些 State 下一个节点拿到的 State 变成了什么看清这条链路Memory 就不是黑盒。