LLM智能体Zero-Mem架构:基于向量数据库与摘要提炼实现零成本记忆
在实际 LLM 应用开发中智能体的记忆能力是决定其能否进行连贯、深度对话和复杂任务执行的关键。传统方案通常将历史对话或任务上下文以文本形式完整地喂给模型这直接导致 token 消耗量线性增长成本飙升并可能因上下文窗口限制而丢失早期关键信息。Zero-Mem 方案的核心思想正是为了解决这一痛点它旨在设计一种机制让智能体能够“记住”过去却无需在每次交互中都为这些记忆支付 token 成本。这不仅仅是成本优化更是提升智能体长期任务执行能力和用户体验的工程实践。本文将深入探讨一种实现“零 token”记忆的可行架构。我们将从理解智能体记忆的必要性与挑战开始逐步构建一个基于向量数据库与摘要提炼的双层记忆系统。这个系统会学习如何筛选关键信息存入长期记忆并在需要时高效、精准地检索而非简单罗列所有历史。最终你将掌握一套可集成到现有 LLM 应用框架中的记忆模块实现方案它能在显著降低 token 消耗的同时保持甚至增强智能体的上下文感知与连贯性。1. 理解智能体记忆从全量上下文到高效存储在深入代码之前必须厘清智能体记忆的本质和传统方案的局限。这决定了我们为何要走向“Zero-Mem”的架构设计。1.1 记忆是什么状态、历史与知识对于 LLM 智能体而言“记忆”并非单一概念它可以被拆解为三个层次会话记忆当前单次对话轮次中的上下文通常直接由模型的上下文窗口承载。短期/工作记忆最近若干轮对话的详细记录用于维持话题的即时连贯性。长期记忆从历史交互中提炼出的关键事实、用户偏好、任务状态和决策依据需要在跨越长时间或多次会话后仍能被访问。传统做法是将短期记忆甚至全部历史作为提示词的一部分这带来了两个核心问题Token 爆炸与信息稀释。随着对话进行宝贵的上下文窗口被越来越多的历史细节占据留给模型处理当前问题和新指令的空间被压缩且早期的重要信息可能因位置靠后而影响力减弱。1.2 Zero-Mem 的设计目标与核心思路Zero-Mem 方案的目标是打破“记忆消耗 token”的强关联。其核心思路是外部化存储将记忆尤其是长期记忆从 LLM 的提示词中剥离存储到外部系统如数据库。摘要与提炼不是存储原始对话而是存储经过 LLM 处理后的、高信息密度的摘要或结构化记录。按需检索在需要记忆辅助时通过查询从外部存储中精准召回相关片段再以少量 token 的形式注入当前上下文。这样日常交互不携带历史负担仅在需要时支付少量“检索与注入”的 token 成本从而实现整体上的“零 token”记忆效果此处“零”是一个目标导向的表述指不随对话线性增长。2. 构建 Zero-Mem 系统架构与核心组件一个典型的 Zero-Mem 系统包含以下几个核心组件我们将以 Python 为例使用 LangChain 等流行库来构建概念模型。2.1 系统总体架构用户输入 | v [对话处理器] ---- [LLM 核心] ---- 生成响应 | | | | v v [记忆管理器] [响应输出] | |-- [记忆提取器]从当前对话中提取关键信息 |-- [向量存储]存储记忆嵌入支持语义检索 |-- [记忆检索器]根据当前查询召回相关记忆 |-- [记忆摘要器]定期或按需压缩记忆防止膨胀2.2 环境准备与依赖配置首先确保你的 Python 环境建议 3.8并安装必要库。我们将使用langchain作为智能体框架chromadb作为轻量级向量数据库openai或langchain-openai作为 LLM 接入也可替换为其他模型。# 创建并激活虚拟环境可选 python -m venv venv source venv/bin/activate # Linux/Mac # venv\Scripts\activate # Windows # 安装核心依赖 pip install langchain langchain-openai chromadb tiktoken # 如需使用 OpenAI 模型设置你的 API 密钥环境变量 # export OPENAI_API_KEYyour-api-key-here # Linux/Mac # set OPENAI_API_KEYyour-api-key-here # Windows2.3 核心组件一记忆提取器与记忆对象记忆不是原始消息的堆砌。我们需要定义“记忆”的数据结构并编写一个提取器来生成它。from pydantic import BaseModel, Field from datetime import datetime from typing import Optional, List from langchain_core.messages import BaseMessage class MemoryEntity(BaseModel): 记忆实体表示一条独立的记忆 id: str Field(default_factorylambda: str(uuid.uuid4())) content: str # 记忆的文本内容是提炼后的摘要 embedding: Optional[List[float]] None # 文本的向量表示 metadata: dict Field(default_factorydict) # 元数据如时间、来源、类型、重要性 created_at: datetime Field(default_factorydatetime.now) last_accessed_at: Optional[datetime] None class Config: arbitrary_types_allowed True class MemoryExtractor: 从对话或LLM响应中提取关键记忆 def __init__(self, llm): self.llm llm def extract_from_conversation(self, messages: List[BaseMessage]) - List[MemoryEntity]: 从一系列消息中提取记忆。 策略让LLM分析对话总结出需要长期记住的事实、用户声明或决策。 # 将最近的对话历史拼接成文本 recent_context \n.join([f{msg.type}: {msg.content} for msg in messages[-5:]]) # 只看最近5条 prompt f 请分析以下对话并提取出需要被智能体长期记住的关键信息。 这些信息包括用户明确陈述的个人偏好如“我喜欢用黑暗模式”、 达成的一致结论或事实如“我们决定每周三开会”、 重要的任务状态更新如“项目A已完成需求评审”。 请用简洁、客观的陈述句列出每条记忆独立成点。 对话记录 {recent_context} 提取出的长期记忆每条记忆用‘- ’开头 try: response self.llm.invoke(prompt) memory_texts [line.strip()[2:] for line in response.content.split(\n) if line.startswith(- )] memories [] for text in memory_texts: if text: # 过滤空行 mem MemoryEntity( contenttext, metadata{ source: conversation_extraction, importance: medium, # 可让LLM进一步评分 context_window: str(messages[-1])[:100] # 关联来源片段 } ) memories.append(mem) return memories except Exception as e: print(f记忆提取失败: {e}) return []关键解释MemoryEntity是记忆的基本单元包含内容、向量和元数据。metadata字段非常灵活可以存储任何有助于检索和管理的标签。MemoryExtractor的核心是使用一个较小的、成本较低的 LLM或大模型的简洁模式来扮演“记忆编辑”的角色从冗长的对话中抓取要点。这步本身消耗 token但其产出记忆是高度浓缩的为后续的“零 token”使用打下基础。提取策略可以更复杂例如区分“事实记忆”、“偏好记忆”、“任务记忆”等。2.4 核心组件二向量存储与记忆检索器提取的记忆需要被存储并能被语义搜索。我们使用 ChromaDB。import chromadb from chromadb.config import Settings from langchain_openai import OpenAIEmbeddings class MemoryStore: 记忆存储与检索管理器 def __init__(self, persist_directory./memory_db): # 初始化 Chroma 客户端持久化到本地目录 self.client chromadb.PersistentClient(pathpersist_directory) # 获取或创建集合。集合名可区分不同用户或智能体实例。 self.collection self.client.get_or_create_collection(nameagent_memories) # 初始化嵌入模型用于将文本转换为向量 self.embedder OpenAIEmbeddings(modeltext-embedding-3-small) # 使用小模型以节约成本 def add_memories(self, memories: List[MemoryEntity]): 添加一批记忆到存储并计算其向量 if not memories: return ids [] documents [] metadatas [] for mem in memories: # 为记忆生成向量嵌入 mem.embedding self.embedder.embed_query(mem.content) ids.append(mem.id) documents.append(mem.content) # ChromaDB 的 metadata 需要是标量或标量列表 flat_metadata {**mem.metadata, created_at: mem.created_at.isoformat()} if mem.last_accessed_at: flat_metadata[last_accessed_at] mem.last_accessed_at.isoformat() metadatas.append(flat_metadata) # 批量添加到向量数据库 self.collection.add( idsids, documentsdocuments, metadatasmetadatas, embeddings[mem.embedding for mem in memories] ) def retrieve_relevant_memories(self, query: str, n_results: int 3) - List[MemoryEntity]: 根据当前查询通常是用户最新问题或对话上下文检索最相关的记忆。 # 生成查询的向量 query_embedding self.embedder.embed_query(query) # 执行相似性搜索 results self.collection.query( query_embeddings[query_embedding], n_resultsn_results ) # 将结果转换回 MemoryEntity 对象 retrieved_memories [] if results[documents]: for i in range(len(results[documents][0])): mem MemoryEntity( idresults[ids][0][i], contentresults[documents][0][i], metadataresults[metadatas][0][i] or {}, ) # 注意从数据库取出的 embedding 可能不是原始对象这里简化处理 retrieved_memories.append(mem) return retrieved_memories def update_memory_access(self, memory_id: str): 更新记忆的最后访问时间可用于实现基于热度的记忆管理 # 这是一个简化示例。实际 ChromaDB 更新 metadata 需要更复杂的操作。 # 一种替代方案是定期运行一个“记忆重要性重评估”任务。 pass关键解释向量化使用嵌入模型如text-embedding-3-small将文本记忆转换为高维向量。语义相似的记忆在向量空间中距离更近。语义检索当用户提出新问题时将问题也向量化然后在向量空间中查找最相似的记忆。这比关键词匹配更能理解意图。元数据过滤chromadb支持基于元数据的过滤例如你可以只检索metadata[type] user_preference的记忆实现更精细的控制。2.5 核心组件三记忆摘要器与记忆管理策略长期记忆库不能无限膨胀也需要更新。我们需要一个“记忆摘要器”来合并、压缩旧的或相似记忆。class MemorySummarizer: 合并与压缩记忆防止记忆库无限增长 def __init__(self, llm): self.llm llm def summarize_similar_memories(self, memory_cluster: List[MemoryEntity]) - MemoryEntity: 将一组高度相似的记忆合并成一条更精炼、更全面的记忆。 例如多次提到“用户喜欢咖啡”可以合并为“用户对咖啡有强烈偏好尤其喜欢拿铁”。 if not memory_cluster: return None if len(memory_cluster) 1: return memory_cluster[0] cluster_contents [mem.content for mem in memory_cluster] prompt f 以下是智能体记录的一组关于同一主题或相似事实的记忆片段。它们可能存在重复或互补信息。 你的任务是将它们融合成一条准确、简洁、信息完整的单一记忆陈述。 原始记忆片段 {chr(10).join([- c for c in cluster_contents])} 请输出融合后的记忆内容只需输出内容本身不要加引号或标记 try: response self.llm.invoke(prompt) summarized_content response.content.strip() # 创建新的记忆实体继承最重要的元数据如最早的创建时间 new_memory MemoryEntity( contentsummarized_content, metadata{ source: summarization, original_ids: [mem.id for mem in memory_cluster], importance: high # 摘要后的记忆通常更重要 } ) return new_memory except Exception as e: print(f记忆摘要失败: {e}) return None记忆管理策略定期摘要可以设置一个后台任务每周或每积累一定数量记忆后运行聚类算法如 K-means 对向量聚类然后对每个簇进行摘要。重要性衰减在metadata中维护一个importance_score根据访问频率、新鲜度和用户反馈动态调整。定期清理分数过低的记忆。冲突检测当新提取的记忆与旧记忆在语义上冲突时例如用户先说“喜欢A”后说“讨厌A”需要设计解决策略如信任最新记忆或标记为“用户偏好变更”。3. 集成与工作流让 Zero-Mem 在智能体中运行现在我们将上述组件组装到一个简单的对话智能体中。3.1 智能体主循环与记忆集成from langchain_openai import ChatOpenAI from langchain_core.messages import HumanMessage, AIMessage, SystemMessage from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder class ZeroMemAgent: def __init__(self, system_prompt: str): # 初始化 LLM self.llm ChatOpenAI(modelgpt-4o-mini, temperature0.1) # 使用轻量模型降低成本 self.system_prompt system_prompt # 初始化记忆系统 self.memory_store MemoryStore() self.memory_extractor MemoryExtractor(self.llm) # 可以用一个更小的模型 self.memory_summarizer MemorySummarizer(self.llm) # 维护一个简单的对话缓冲区短期记忆 self.conversation_buffer: List[BaseMessage] [] def _build_prompt_with_memory(self, user_input: str, retrieved_memories: List[MemoryEntity]) - ChatPromptTemplate: 构建包含系统指令、长期记忆和对话上下文的提示词 # 将检索到的记忆格式化成文本 memory_context if retrieved_memories: memory_context 以下是你之前记住的关于用户和相关事务的信息请在处理当前对话时参考\n memory_context \n.join([f- {mem.content} for mem in retrieved_memories]) memory_context \n\n # 构建完整的系统消息 full_system_message f{self.system_prompt} {memory_context} 当前对话历史最近几轮 # 使用 LangChain 的提示模板 prompt ChatPromptTemplate.from_messages([ (system, full_system_message), MessagesPlaceholder(variable_nameconversation_history), (human, {input}), ]) return prompt def invoke(self, user_input: str) - str: 处理用户输入的一轮交互 # 1. 检索相关长期记忆 retrieved_mems self.memory_store.retrieve_relevant_memories(user_input, n_results2) print(f[DEBUG] 检索到 {len(retrieved_mems)} 条相关记忆) # 2. 构建包含记忆的提示词并调用LLM prompt self._build_prompt_with_memory(user_input, retrieved_mems) chain prompt | self.llm # 准备对话历史这里用 buffer 的最后几轮作为短期上下文 recent_history self.conversation_buffer[-6:] # 保留最近3轮对话6条消息 response_message chain.invoke({ conversation_history: recent_history, input: user_input }) ai_response response_message.content # 3. 更新对话缓冲区 self.conversation_buffer.append(HumanMessage(contentuser_input)) self.conversation_buffer.append(AIMessage(contentai_response)) # 可选限制缓冲区长度防止内存泄漏 if len(self.conversation_buffer) 20: self.conversation_buffer self.conversation_buffer[-20:] # 4. 从本轮交互中提取新的长期记忆 # 通常从完整的本轮交互用户输入AI响应中提取这里简化仅从对话buffer末尾提取 new_memories self.memory_extractor.extract_from_conversation(self.conversation_buffer[-4:]) # 看最近两轮 if new_memories: print(f[DEBUG] 提取到 {len(new_memories)} 条新记忆) self.memory_store.add_memories(new_memories) # 5. 可选触发记忆整理任务例如每N轮运行一次 # if len(self.conversation_buffer) % 10 0: # self._run_memory_maintenance() return ai_response def _run_memory_maintenance(self): 示例性的记忆维护任务 # 这是一个高级功能可能涉及 # 1. 对旧记忆进行聚类和摘要 # 2. 清理低重要性记忆 # 3. 更新记忆的元数据如重要性分数 print(执行记忆维护...) # 实现略3.2 运行一个完整的对话示例# 初始化智能体 system_prompt 你是一个有帮助的助手。请根据你记住的关于用户的信息和当前的对话历史提供准确、有用的回答。 agent ZeroMemAgent(system_prompt) # 模拟多轮对话 conversation [ “我喜欢喝拿铁咖啡不喜欢美式。”, “记住了。另外我计划下周三下午3点去健身房。”, “我之前的健身计划是什么来着还有推荐一款咖啡。” ] print(用户: conversation[0]) response1 agent.invoke(conversation[0]) print(AI: response1) print(---) print(用户: conversation[1]) response2 agent.invoke(conversation[1]) print(AI: response2) print(---) print(用户: conversation[2]) response3 agent.invoke(conversation[2]) print(AI: response3)预期效果 在第三轮中用户的问题“我之前的健身计划是什么来着”和“推荐一款咖啡”都需要依赖前两轮的长期记忆。Zero-Mem 系统会在处理第三轮输入时将问题“我之前的健身计划是什么来着还有推荐一款咖啡。”进行向量化。从向量存储中检索出最相关的两条记忆“用户喜欢喝拿铁咖啡不喜欢美式”和“用户计划下周三下午3点去健身房”。将这两条记忆可能只有几十个 token插入到第三轮的提示词中。LLM 基于这个富含关键记忆的上下文生成连贯、个性化的回答“你计划下周三下午3点去健身房。关于咖啡既然你喜欢拿铁我推荐你尝试一下燕麦拿铁口感更醇厚。”可以看到智能体“记得”之前的信息但第三轮的提示词并没有包含第一、二轮的全部原始对话可能上百 token而只是注入了两条精炼的记忆。这就是“Zero-Mem”的核心价值。4. 关键参数、配置与性能调优实现基础功能后需要关注系统的可配置性和性能。4.1 核心参数说明参数/组件常见配置与建议值作用与影响记忆提取器MemoryExtractor触发时机每轮/每N轮/检测到关键信息后。提取条数1-3条。决定什么信息被存入长期记忆。过于频繁或宽松会导致记忆库膨胀且冗余过于保守会导致重要信息丢失。建议在用户明确陈述事实、偏好或决策时触发。向量嵌入模型text-embedding-3-small(OpenAI),BAAI/bge-small-zh(开源)。影响记忆检索的语义准确性。更大的模型效果更好但更慢更贵。对于多数对话场景小模型已足够。需注意与主 LLM 的语言一致性。检索数量n_results2-5条。每次查询注入多少条记忆到上下文。太少可能遗漏关键信息太多会占用有效上下文窗口并可能引入噪声。可以从2开始根据任务复杂度调整。记忆摘要触发条件记忆数量阈值如1000条、定期任务如每天、或相似度阈值聚类。控制记忆库的规模和信息密度。不及时摘要会导致检索效率下降和存储成本上升。对话缓冲区长度最近3-10轮6-20条消息。作为短期记忆维持对话的即时流畅性。太长会挤占用于长期记忆和当前思考的 token太短会导致对话不连贯。4.2 生产环境考量持久化与备份ChromaDB的本地持久化是基础。生产环境应考虑定期备份.persist_directory或使用支持远程、高可用的向量数据库如Pinecone,Weaviate,Qdrant。异步处理记忆提取、向量化、存储和摘要都是相对耗时的 I/O 或网络操作。不应阻塞主对话线程。应使用消息队列或异步任务如Celeryasyncio来处理这些后台任务。错误处理与降级LLM 提取记忆可能失败向量数据库可能超时。系统应具备降级能力例如当记忆检索失败时直接使用空的记忆上下文并记录日志告警保证核心对话功能不中断。监控与评估Token 节省率(原始历史对话 token 数 - 实际使用记忆 token 数) / 原始历史对话 token 数。这是衡量 Zero-Mem 经济效益的核心指标。记忆召回准确率人工抽样评估检索到的记忆是否真正相关。记忆提取质量定期检查自动提取的记忆内容是否准确、无歧义。5. 常见问题排查与优化实践在实际部署中你可能会遇到以下问题。5.1 问题一智能体“忘记”了明明记录过的重要信息现象用户提及过去的关键事实但 AI 回答中未体现仿佛从未听说过。可能原因与排查记忆未被成功提取检查MemoryExtractor的日志看当时是否因 LLM 调用失败或解析错误而未生成任何MemoryEntity。优化提取提示词使其更稳定。记忆未被成功存储检查向量数据库add操作是否返回错误。确认嵌入模型调用是否成功向量维度是否与集合配置匹配。检索相关性低用户查询的表述与记忆内容的表述差异太大导致向量相似度低。例如用户问“我上次说的那个咖啡爱好”记忆里存的是“用户喜欢喝拿铁咖啡”。可以尝试查询扩展在检索前先用 LLM 将用户查询重写或扩展成几个语义相近的表述用这些表述去并行检索。混合检索结合关键词BM25和向量语义检索提高召回率。调整元数据过滤确保没有错误的元数据过滤条件排除了相关记忆。记忆被摘要合并或清理检查记忆摘要策略是否过于激进将独特的重要记忆与其它记忆合并导致信息丢失。调整摘要的相似度阈值或设置重要记忆的“保护”标签。5.2 问题二注入记忆后AI 响应变得混乱或偏离主题现象AI 的回答开始胡言乱语或者过度关注记忆中的次要细节。可能原因与排查记忆注入位置不当确保记忆被放在系统提示词或上下文中的明确位置如“以下是背景信息”之后与当前指令和对话历史清晰区隔。避免记忆和指令混杂。记忆内容质量差检查提取的记忆是否包含不完整、矛盾或带有误导性的信息。优化MemoryExtractor的提示词要求输出客观、简洁、完整的陈述句。检索了过多或不相关记忆减少n_results参数。在注入前可以增加一个“记忆相关性重排序”步骤用一个小模型对检索结果打分只保留分数最高的1-2条。记忆与系统指令冲突系统指令应明确告知 AI 如何利用这些记忆例如“请参考以下背景信息但优先遵循用户的最新指令。”5.3 问题三系统延迟明显增加现象用户感到响应变慢。可能原因与排查同步向量化在add_memories和retrieve_relevant_memories中嵌入模型的调用是同步的。将其改为异步或使用嵌入模型的批处理 API。向量数据库性能本地 ChromaDB 在记忆条数巨大10万时检索可能变慢。考虑对记忆进行分区按用户、按时间。使用更专业的向量数据库。建立索引如 HNSW。记忆提取过于频繁调整为每 N 轮对话或检测到特定关键词时才触发提取而非每轮都触发。5.4 最佳实践清单启动检查清单[ ] 嵌入模型 API 密钥或本地模型路径配置正确。[ ] 向量数据库连接正常集合已创建。[ ] 记忆存储目录有写入权限。[ ] 主 LLM 和用于记忆提取/摘要的 LLM 的模型名称、参数配置无误。提示词工程优化提取提示词明确指令模型提取“客观事实”、“用户明确声明的偏好”、“已确认的任务细节”避免提取猜测、疑问或临时性内容。摘要提示词指令模型进行“合并同类项”、“消除冗余”、“保留核心事实”输出格式严格限定。系统提示词明确告知 AI “以下是辅助你回答的背景信息请酌情参考”并强调当前用户指令的优先级最高。记忆生命周期管理设置 TTL为某些类型的记忆如临时任务状态设置生存时间到期自动清理。重要性评分设计一个评分算法综合记忆的访问频率、新鲜度、来源可信度等定期清理低分记忆。人工审核接口为关键应用提供界面允许管理员查看、编辑或删除自动生成的记忆修正错误。测试与评估构建一个测试集包含需要长期记忆才能正确回答的多轮对话。对比使用全量历史上下文与使用 Zero-Mem 系统时AI 回答的准确性和 token 消耗。进行 A/B 测试评估用户体验的差异。6. 扩展方向与进阶思考基础的 Zero-Mem 系统搭建完成后可以考虑以下方向进行深化分层记忆结构引入“情景记忆”与特定会话或任务链绑定、“语义记忆”通用知识和“程序性记忆”常用操作流程设计不同的存储、检索和失效策略。记忆主动触发不仅被动响应用户查询智能体可以主动在对话中提及相关记忆例如“根据您之前提到的对咖啡的喜好我想到...”这需要更复杂的记忆相关性判断机制。多模态记忆支持存储和检索图像、音频的描述性向量构建更丰富的用户画像。联邦记忆与隐私在严格保护用户隐私的场景下研究如何在本地设备上存储和处理记忆仅向云端发送必要的、脱敏的查询。与现有框架深度集成将本方案封装成LangChain或LlamaIndex的Memory类使其可以无缝接入更复杂的智能体工作流。实现真正的“零 token”记忆是一个权衡的艺术需要在记忆的丰富性、准确性、检索速度和成本之间找到最佳平衡点。本文提供的架构是一个坚实的起点通过持续的迭代、监控和调优你可以构建出一个既高效又智能的长期伴侣型 AI 应用。