Day 40记忆与工具调用整合 —— 让 Agent 拥有“记忆感知”能力欢迎来到第四十天过去四天我们逐步构建了完整的记忆系统短期记忆管理、Embedding 检索优化、自动记忆抽取、记忆更新与遗忘。但这些组件目前还是“独立”的——它们没有和 Agent 的工具调用深度整合。今天我们将把记忆系统嵌入到 Agent 的核心循环中让 Agent 能够在对话中自动查询记忆、利用记忆做决策、在适当时机更新记忆。这是构建一个真正“有记忆”的智能客服的关键一步。一、今日学习目标理解记忆与 Agent 整合的三种模式记忆作为工具、记忆作为上下文注入、记忆作为后处理。掌握将记忆系统封装为 LangChain Tool 的方法让 Agent 自主决定何时查询和更新记忆。学会在 Agent 执行前自动注入相关记忆到系统提示词中。实现一个完整的“记忆感知型 Agent”它能在多轮对话中记住用户信息并做出个性化回复。通过实战测试观察 Agent 如何结合记忆、工具和推理完成复杂任务。二、详细实现步骤步骤 1理解记忆与 Agent 的三种整合模式模式说明优点缺点适用场景记忆作为工具将记忆的查询和更新封装为 ToolAgent 自主调用灵活Agent 可决定何时使用增加工具调用次数可能延迟复杂 Agent需要精细控制记忆作为上下文注入每次对话前自动检索相关记忆注入到 System Prompt无额外工具调用响应快可能注入无关记忆消耗 Token客服、个人助手等高频场景记忆作为后处理Agent 回复后自动抽取并更新记忆不干扰 Agent 主流程更新延迟可能遗漏所有场景的补充最佳实践组合使用。上下文注入负责“读”后处理负责“写”工具调用作为补充如 Agent 需要主动查询特定记忆时。步骤 2环境准备与基础组件确保已安装所有必要库并初始化模型、Embedding、向量库。importosimportuuidfromdatetimeimportdatetimefromdotenvimportload_dotenvfromlangchain_openaiimportChatOpenAI,OpenAIEmbeddingsfromlangchain_community.vectorstoresimportChromafromlangchain_core.toolsimporttoolfromlangchain_core.promptsimportChatPromptTemplate,MessagesPlaceholderfromlangchain_core.messagesimportHumanMessage,AIMessage,SystemMessage,ToolMessagefromlangchain_core.documentsimportDocumentfromlangchain.agentsimportcreate_react_agent,AgentExecutorfromlangchain_core.promptsimportPromptTemplatefromtypingimportAnnotated load_dotenv()llmChatOpenAI(modeldeepseek-chat,api_keyos.getenv(DEEPSEEK_API_KEY),base_urlhttps://api.deepseek.com,temperature0.1)embeddingsOpenAIEmbeddings(modelembedding-2,api_keyos.getenv(ZHIPU_API_KEY),base_urlhttps://open.bigmodel.cn/api/paas/v4)vectorstoreChroma(collection_nameagent_memories,embedding_functionembeddings,persist_directory./agent_memory_db)步骤 3将记忆查询封装为 Tool虽然我们会自动注入记忆但 Agent 有时需要主动查询特定记忆如“帮我查一下我之前说的偏好”。我们定义两个工具recall_memory检索记忆和save_memory保存记忆。tooldefrecall_memory(query:Annotated[str,查询关键词或问题用于检索相关记忆])-str:从长期记忆中检索与查询相关的信息。当你需要回忆用户之前提到过的偏好、事实或事件时使用此工具。resultsvectorstore.similarity_search(query,k3,filter{status:active})ifnotresults:return未找到相关记忆。lines[]fordocinresults:mem_typedoc.metadata.get(memory_type,unknown)lines.append(f[{mem_type}]{doc.page_content})return\n.join(lines)tooldefsave_memory(content:Annotated[str,要保存的记忆内容简洁明确],memory_type:Annotated[str,记忆类型fact事实、preference偏好、event事件])-str:将重要信息保存到长期记忆中。当你发现用户提供了值得记住的新信息时使用此工具。memory_idstr(uuid.uuid4())docDocument(page_contentcontent,metadata{user_id:current_user,memory_type:memory_type,importance:3,timestamp:datetime.now().isoformat(),status:active,memory_id:memory_id,access_count:0})vectorstore.add_documents([doc])returnf已保存记忆{content}步骤 4实现自动记忆注入在每次调用 Agent 之前根据用户输入检索相关记忆并注入到 System Prompt 中。这样 Agent 无需主动调用工具就能“知道”用户的背景信息。defretrieve_and_format_memories(user_input:str,user_id:strcurrent_user,k:int3)-str:检索相关记忆并格式化为文本resultsvectorstore.similarity_search(user_input,kk,filter{user_id:user_id,status:active})ifnotresults:return暂无相关记忆。lines[]fordocinresults:mem_typedoc.metadata.get(memory_type,unknown)lines.append(f[{mem_type}]{doc.page_content})return\n.join(lines)defbuild_system_prompt_with_memories(user_input:str)-str:构建包含记忆的系统提示词memories_textretrieve_and_format_memories(user_input)returnf你是一个智能客服助手名叫小智。你能够记住用户的信息并提供个性化服务。 以下是你当前知道的关于该用户的信息来自长期记忆{memories_text}请根据这些信息以及用户当前的问题提供准确、个性化的回复。如果记忆中没有相关信息不要编造。 步骤 5构建记忆感知型 Agent我们将记忆注入、工具调用和 AgentExecutor 整合起来。每次对话时根据用户输入检索记忆注入 System Prompt。运行 AgentAgent 可以调用工具包括记忆工具。Agent 回复后自动抽取新记忆并更新。# 定义工具列表tools[recall_memory,save_memory]# ReAct 提示词模板react_template{system_prompt} 你可以使用以下工具 {tools} 工具名称列表{tool_names} 使用以下格式进行推理和行动 问题用户提出的问题 思考你应该思考接下来要做什么 行动要使用的工具名称必须是 [{tool_names}] 中的一个 行动输入传递给工具的输入 观察工具返回的结果 ...这个思考/行动/行动输入/观察可以重复 N 次 思考我现在知道最终答案了 最终答案对用户问题的最终回答 开始 问题{input} 思考{agent_scratchpad}promptPromptTemplate.from_template(react_template)defcreate_memory_aware_agent(user_input:str):创建一个带记忆注入的 Agent 并执行# 1. 构建带记忆的系统提示system_promptbuild_system_prompt_with_memories(user_input)# 2. 创建 Agentagentcreate_react_agent(llm,tools,prompt)executorAgentExecutor(agentagent,toolstools,verboseTrue,handle_parsing_errorsTrue,max_iterations5)# 3. 执行resultexecutor.invoke({input:user_input,system_prompt:system_prompt})returnresult[output]步骤 6实现后处理记忆更新Agent 回复后自动从对话中抽取新记忆并存入长期记忆。复用 Day 38 的抽取逻辑但简化为直接调用 LLM。extract_promptChatPromptTemplate.from_messages([(system,从以下对话中抽取值得长期记住的用户信息。只输出 JSON 数组每个元素包含 content、memory_type、importance。 memory_type 可选fact、preference、event。 importance 为 1-5。 如果没有值得记住的信息输出 []。 示例 用户我的订单号是 12345。 AI好的我记下了。 输出[{content: 用户的订单号是 12345, memory_type: fact, importance: 4}] ),(human,用户{user_input}\nAI{ai_response})])extract_chainextract_prompt|llm|StrOutputParser()defpost_process_memories(user_input:str,ai_response:str):后处理抽取新记忆并存入向量库try:resultextract_chain.invoke({user_input:user_input,ai_response:ai_response})importjson memoriesjson.loads(result)formeminmemories:# 去重检查简化检索相似度existingvectorstore.similarity_search_with_score(mem[content],k1,filter{status:active})ifexistingandexisting[0][1]0.15:# 距离很小说明重复continuedocDocument(page_contentmem[content],metadata{user_id:current_user,memory_type:mem.get(memory_type,fact),importance:mem.get(importance,3),timestamp:datetime.now().isoformat(),status:active,memory_id:str(uuid.uuid4()),access_count:0})vectorstore.add_documents([doc])print(f [记忆更新] 存入{mem[content]})exceptExceptionase:print(f记忆抽取失败{e})步骤 7整合完整对话流程defchat(user_input:str,user_id:strcurrent_user):完整的记忆感知对话流程print(f\n用户{user_input})# 1. 记忆注入 Agent 执行ai_responsecreate_memory_aware_agent(user_input)# 2. 后处理抽取新记忆post_process_memories(user_input,ai_response)returnai_response步骤 8测试完整流程模拟一个多轮对话场景观察 Agent 如何利用记忆if__name____main__:# 第一轮用户提供信息response1chat(你好我叫张三我的订单号是 12345我喜欢科幻小说。)print(f小智{response1})# 第二轮Agent 应该记住用户信息response2chat(你还记得我叫什么吗我的订单号是多少)print(f小智{response2})# 第三轮Agent 应该能利用偏好推荐response3chat(推荐一本我可能喜欢的书。)print(f小智{response3})# 第四轮用户更新偏好response4chat(其实我现在更喜欢历史小说了。)print(f小智{response4})# 第五轮验证偏好更新response5chat(我现在喜欢什么类型的书)print(f小智{response5})预期观察第一轮Agent 回复并自动抽取记忆姓名、订单号、偏好。第二轮Agent 从注入的记忆中看到用户信息直接回答。第三轮Agent 结合偏好推荐科幻书籍。第四轮Agent 更新偏好记忆。第五轮Agent 检索到更新后的偏好回答“历史小说”。三、常见问题与调试Q1记忆注入导致 System Prompt 过长Token 消耗大。→ 限制检索记忆的数量k3并在格式化时截断每条记忆的长度如最多 80 字。也可以只注入重要性 3 的记忆。Q2Agent 同时使用记忆工具和自动注入造成信息重复。→ 可以在 System Prompt 中说明“以下信息已自动提供无需再次调用 recall_memory 工具”或者只在 Agent 明确需要查询特定记忆时才使用工具。Q3后处理抽取的记忆与已有记忆重复。→ 在存入前做相似度检查距离小于阈值如 0.15则跳过。也可以让抽取 Prompt 参考已有记忆列表但这会增加 Token 消耗。实践中简单的相似度去重已经足够。Q4Agent 在回复中“假装”记得用户信息但实际记忆库中没有。→ 这可能是模型幻觉。在 System Prompt 中强调“只根据提供的记忆回答如果记忆中没有相关信息请明确说不知道”。同时降低温度减少模型自由发挥。Q5多用户场景下如何隔离记忆→ 每个用户有独立的user_id所有记忆操作都带filter{user_id: user_id}。在对话开始时根据用户登录信息确定user_id。当前代码中硬编码了current_user实际项目中应从会话中获取。Q6如何让 Agent 主动询问用户是否要记住某条信息→ 可以在 System Prompt 中加入规则“当你发现用户提供了重要信息如订单号、偏好可以在回复中确认‘我已记住’”。这样用户有感知也便于纠错。Q7记忆注入的检索时机是什么→ 我们在每次用户输入时检索。如果用户输入很短如“嗯”“好的”可以跳过检索复用上一轮的记忆减少不必要的调用。四、今日总结与作业今天你完成了✅ 理解了记忆与 Agent 整合的三种模式工具、上下文注入、后处理。✅ 将记忆查询和保存封装为 LangChain Tool。✅ 实现了自动记忆注入让 Agent 在对话前就“知道”用户背景。✅ 实现了后处理记忆抽取让 Agent 在对话后自动学习。✅ 构建了完整的“记忆感知型 Agent”并通过多轮对话验证了效果。今日作业必做修改build_system_prompt_with_memories增加一个参数include_types可以只注入特定类型的记忆如只注入preference和fact不注入event。测试不同组合的效果。实现一个get_user_profile(user_id)函数从向量库中汇总用户的活跃记忆生成一个结构化的用户画像如{name: 张三, preferences: [科幻小说], facts: [订单号 12345]}。设计一个测试场景用户先问“我的订单到哪了”Agent 没有订单查询工具只能依赖记忆。观察 Agent 是否能从记忆中检索到订单号并做出合理回复如“您的订单号是 12345但我目前无法查询物流请稍后”。思考题在客服 Agent 中记忆注入和后处理抽取都会调用 LLM 或 Embedding增加了延迟。你如何优化这个流程使其在生产环境中保持低延迟请列出至少三个优化方向。明日预告我们将学习如何为客服 Agent 添加真实世界工具——查询订单数据库、调用物流 API、创建退款工单。这将让 Agent 从“能聊天”升级为“能办事”。有任何问题欢迎随时提问