ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

你的 Agent 每天都在失忆:三层记忆架构,一次讲透

你的 Agent 每天都在失忆:三层记忆架构,一次讲透 Agent 长期记忆的三层架构会话记忆、用户偏好、向量记忆各管一层01 没有记忆的 Agent每天都在失忆你肯定遇到过昨天刚告诉 Agent「我们团队用 pnpm 不用 npm」今天它又给你生成一份 package-lock.json。不是它笨是它每次会话结束就失忆——上下文窗口模型一次能「看见」的内容随会话关闭清零之前说过的东西它根本看不见。企业场景里这个问题更致命客服 Agent 记不住客户上一通电话说了什么编程 Agent 每次都要重新了解你的代码规范销售 Agent 面对老客户像面对陌生人。记忆不是加分项是 Agent 能不能在企业里干长工的及格线。解法是把记忆从「模型自带的上下文」里拆出来做成一个独立的、可持久化、可治理的子系统。这一篇给出一套三层记忆架构配完整代码能直接抄进项目。02 三层记忆架构按「记什么、记多久、怎么用」分三层各管一摊层记什么生命周期实现会话记忆本轮对话说了什么会话内随会话存续消息历史 窗口裁剪用户偏好用户的稳定事实偏好/规范/背景长期跨会话结构化 KV 存储显式更新向量记忆历史对话里值得留的片段长期语义可查向量库 语义检索三层的关系不是并列是流水线会话进行中一段对话如果包含稳定事实「我们用 pnpm」就提炼成用户偏好写进 KV如果是有场景价值的片段某次调试的结论就向量化后存进记忆库。下次会话开始时再按需要捞回来注入上下文。一次对话的记忆写入与读取时序为什么用户偏好和向量记忆要分开因为它们的查法不同偏好是「这个人有什么确定的设定」查法是精确取key-value 一查就中向量记忆是「历史上有没有类似的情况」查法是语义相似。混在一起要么偏好被语义检索漏掉要么历史片段干扰确定设定。03 代码实现一个 Memory 模块核心是一个AgentMemory类三个方法对应三层add_message会话、remember偏好、recall检索。用 LangChain 的消息类型 Chroma 向量库 OpenAI embeddingsPythonimport os, json, time from chromadb import Client from chromadb.config import Settings from langchain_openai import OpenAIEmbeddings class AgentMemory: 三层记忆会话窗口 / 用户偏好 KV / 向量记忆库。 def __init__(self, session_id: str, window: int 20, persist_dir: str ./memory_data): self.session_id session_id self.window window # ① 会话记忆内存即可换 Redis 见第 05 节 self.messages: list[dict] [] # ② 用户偏好JSON 文件起步生产换 Redis hash self.pref_path os.path.join(persist_dir, prefs.json) self.prefs self._load_prefs() # ③ 向量记忆Chroma 本地持久化 self.embeddings OpenAIEmbeddings(modeltext-embedding-3-small) self.vdb Client(Settings( persist_directoryos.path.join(persist_dir, vdb)) ).get_or_create_collection(episodes) # ---------- ① 会话记忆 ---------- def add_message(self, role: str, content: str) - None: self.messages.append({role: role, content: content, ts: time.time()}) # 窗口裁剪只保留最近 window 条防止上下文爆掉 self.messages self.messages[-self.window:] def chat_context(self) - list[dict]: 给模型的会话上下文 最近窗口 永远带上的偏好。 return [{role: system, content: 已知用户偏好\n self._prefs_text()} ] self.messages # ---------- ② 用户偏好 ---------- def remember(self, key: str, value: str) - str: 记住一条稳定事实。同 key 直接覆盖偏好以最新为准。 self.prefs[key] {value: value, ts: time.time()} self._save_prefs() return f已记住{key} {value} def _prefs_text(self) - str: return \n.join(f- {k}: {v[value]} for k, v in self.prefs.items()) or 暂无 # ---------- ③ 向量记忆 ---------- def memorize(self, text: str, meta: dict | None None) - str: 把一段有价值的片段存入向量库。 vec self.embeddings.embed_query(text) doc_id f{self.session_id}-{int(time.time()*1000)} self.vdb.add(ids[doc_id], embeddings[vec], documents[text], metadatas[{session: self.session_id, **(meta or {})}]) return doc_id def recall(self, query: str, k: int 3) - list[str]: 按语义捞出最相关的历史片段注入上下文。 vec self.embeddings.embed_query(query) res self.vdb.query(query_embeddings[vec], n_resultsk) return res[documents][0] if res[documents] else []两个值得注意的设计决策窗口裁剪 偏好常驻会话上下文 最近 20 条消息 偏好全文。偏好永远在场它是确定设定丢不得旧消息按窗口淘汰聊天过程丢了就丢了。这个组合拳解决了「既要记得久、又不能撑爆上下文」的矛盾。写入时机靠模型判断remember和memorize不是每轮都调而是作为两个工具挂给 Agent让模型自己判断「这句话值不值得记」——这是记忆系统的第一道闸门下一节展开。04 接到 Agent 上记忆即工具把remember/memorize/recall包装成 LangChain 工具模型就能在对话中自主读写记忆Pythonfrom langchain_core.tools import tool from langchain.agents import create_agent from langchain_openai import ChatOpenAI memory AgentMemory(session_iddayu-001) tool def save_preference(key: str, value: str) - str: 记住用户的稳定偏好或规范。仅在用户明确表达长期约定时调用。 return memory.remember(key, value) tool def save_episode(text: str) - str: 存一段有场景价值的历史片段调试结论/项目背景。 return memory.memorize(text) tool def search_memory(query: str) - str: 回答前先查历史记忆看有没有相关经验。 hits memory.recall(query) return \n.join(hits) if hits else 没有相关历史记忆。 agent create_agent( modelChatOpenAI(modelgpt-4o-mini, temperature0), tools[save_preference, save_episode, search_memory], system_prompt( 你是长期服务用户的助手。规则\n 1. 用户表达长期约定用 pnpm、代码规范等→ 调 save_preference\n 2. 出现有复用价值的结论 → 调 save_episode\n 3. 回答涉及时效或历史经验的问题前 → 先调 search_memory\n 不要把一次性信息今天天气存进记忆。 ), )docstring 里的「仅在……时调用」就是记忆系统的治理规则——模型靠它区分「该记的」和「不该记的」。实践中还要再加一道保险把「记忆写入」事件打到审计日志权限篇同款谁在什么时候记了什么可回溯、可删除——这在企业里不是可选项。05 完整闭环一次带记忆的对话光有工具还不够得把它们串成一次完整的对话流程。下面是「下一轮会话开始 → 注入记忆 → 对话 → 写入 → 结束」的全链路Pythonmemory AgentMemory(session_iddayu-002) # 新会话 messages memory.chat_context() # 已含偏好常驻 # 用户问了一个和历史上相似的问题 user_q 这个项目该用哪个包管理器装依赖 # ① 先检索向量记忆有没有类似的历史经验 hits memory.recall(user_q, k2) if hits: messages.append({role: system, content: 相关历史经验\n- \n- .join(hits)}) # ② 再加用户当前问题交给模型 messages.append({role: user, content: user_q}) # 模型看到的三层内容 # [system] 已知用户偏好- 包管理器: pnpm ← 偏好层常驻 # [system] 相关历史经验- 上次 pnpm install 报错是 node 版本… ← 向量层按需 # [user] 这个项目该用哪个包管理器装依赖 ← 会话层当前输入模型回答时就会带上记忆「你们团队约定用 pnpm偏好另外上次用 pnpm 装依赖时踩过 Node 版本的坑向量记忆建议先确认版本。」注入的优先级要记住偏好 向量检索 会话窗口。偏好是用户拍板的确定设定检索只是参考经验——如果历史片段和偏好冲突历史里用过 npm但后来约定改 pnpm以偏好为准。实现上就是在拼 prompt 时给两段 system 内容标注不同权重描述。写入侧也一样有讲究。不是所有对话都值得记判断标准写进 system_prompt 之后还可以在工具内部再挡一道PythonSENSITIVE_KEYS {password, 手机号, 身份证, api_key} tool def save_preference(key: str, value: str) - str: 记住用户的稳定偏好或规范。仅在用户明确表达长期约定时调用。 if any(s in key.lower() or s in value for s in SENSITIVE_KEYS): return 该内容涉及敏感信息已拒绝存入记忆。 # 脱敏闸门 return memory.remember(key, value)模型判断 工具闸门双保险提示词被注入改写时权限篇讲过的风险敏感信息也进不了记忆库。06 企业级落地三个硬问题Demo 到生产记忆系统要过三关① 持久化与并发list和本地 JSON 撑不起多副本。生产形态是「Redis会话偏好 Milvus/pgvector向量」数据存储要点会话消息Redis Listkeysession:{id}:msgsTTL 30 天自动过期用户偏好Redis Hashkeyuser:{id}:prefs随用户生命周期向量记忆Milvus / pgvectormetadata 带 user_id检索强制过滤AgentMemory的接口不变换实现即可——这也是当初把三层封装在一个类里的回报。② 记忆治理记忆会腐烂——用户换了团队旧规范还在被注入。两个机制过期复核偏好带updated_at超过 90 天未确认的下会话开头主动问一句「你们现在还在用 pnpm 吗」——一条提示就能防止过期规范污染回答可删除向量记忆的 metadata 里存user_id和session_id用户要求「忘掉我说过的」时能按用户维度精准删除GDPR/个保法的硬要求不是加分项③ 隐私与隔离三个红线——用户之间记忆严格隔离查询强制按user_id过滤Chroma/Milvus 里写成 where 条件不能靠 prompt 自觉敏感信息不入库上面的脱敏闸门记忆库的访问权限跟随业务系统不因为 Agent 有最高权限就连带记忆全裸奔。一句话记忆系统的架构不难难的是治理——什么该记、记多久、谁能看、怎么删这四个问题想清楚了代码反而是最简单的部分。一句话总结Agent 的长期记忆 会话窗口 偏好 KV 向量库三层流水线架构一天能写完治理记什么/记多久/怎么删才决定它能不能进企业。
返回列表