拓冰建站拓冰建站
首页 / 资讯中心 / 正文

零Token记忆操作:LLM Agent长对话记忆管理范式解析

如果你正在做 LLM Agent 开发大概率已经撞上过同一个问题对话一长token 先爆了记忆一多上下文拼装越来越慢为了“让 Agent 记住”每轮都要把历史记录、检索片段、工具结果全部塞进 prompt。这个方向里最近出现了一个值得细看的思路——Zero-Mem: Zero-Token Memory Operations for LLM Agents。核心目标很直接让 Agent 的记忆读写操作本身不再消耗 LLM 上下文 token把“记忆动作”和“推理上下文”彻底解耦。这篇文章不打算只做概念解读。我会把这个设计思路拆成可以落地的方法论给出一套参考实现、测试维度、接口化方案和排查清单。无论你是已经在用 LangChain、自研 Agent 框架还是正在做 RAG 场景读完都可以拿这套思路去改造自己的记忆层。先给结论Zero-Mem 不是某个可以直接pip install的固定软件包而是一类记忆管理范式的名字。它的核心卖点有三个记忆操作不消耗推理 token、记忆存储与模型推理解耦、通过检索和元数据控制注入内容。要做到这些不需要特殊的硬件也不需要重写大模型推理逻辑需要的是一套边界清晰的记忆管理层设计。文章会从这几个方面展开先讲清楚它解决什么痛点再看适用边界然后给出环境准备、参考实现、功能测试、接口化与批量任务、性能观察、常见问题排查最后是工程化建议。适合正在做 Agent 应用、RAG 系统、多轮对话产品以及关心 token 成本和系统稳定性的开发者。1. 核心能力速览先把这个设计范式的关键特性整理成一张表方便快速判断它适不适合你的场景。能力项说明项目类型LLM Agent 记忆管理设计范式 / 方法论要解决的问题记忆读写占用上下文 token、长对话上下文膨胀、历史检索效率低核心思路把记忆的写、读、改、删从 LLM 上下文中剥离由外部记忆层完成Token 消耗记忆操作本身不消耗推理 token仅最终注入的关联片段占用上下文硬件要求无特殊硬件要求依赖 Agent 原本的运行环境依赖环境Python、Agent 框架LangChain 等或自研、可选向量数据库启动方式不需要单独启动服务以代码库/模块方式集成也可做成独立 Memory Service是否支持 API可以按需要将记忆层封装为独立服务是否支持批量任务可以批量写入、批量索引、批量归档均适用适合场景多轮对话 Agent、长会话应用、RAG 系统、多 Agent 协作主要落地难点记忆项切分、相关性召回、注入策略、隐私与权限控制需要明确一点因为这类设计通常是“框架内置能力”或“方案论文”不同项目在实现上差异很大具体指标要以你参考的仓库、论文或框架文档为准。下面所有代码都是按这个设计思路整理出来的教学示例用来跑通流程不是某个特定仓库的搬运。2. 适用场景与使用边界2.1 适合谁用Zero-Token 记忆操作最直接的受益场景是长会话 Agent。传统做法里每次调用模型都要把历史记录完整拼接进 prompt对话轮次一多token 成本线性上涨响应延迟也跟着涨。把记忆操作外部化之后Agent 每轮只接收“和当前任务相关的记忆片段”历史数据被隔离在记忆存储中上下文始终是可控的。另一个典型场景是 RAG 系统。知识库检索结果通常包含多段文档直接全部注入会导致关键信息被稀释。如果把“知识记忆”和“对话记忆”分开管理再用相关性筛选和摘要压缩最终注入的 token 数量能明显下降。多 Agent 协作场景也值得关注。不同 Agent 之间共享记忆时如果直接互相传对话全文通信成本会非常高。通过外部记忆层共享结构化记忆各 Agent 按需拉取可以显著降低多轮通信的开销。2.2 不适合什么场景如果你只是做一个单轮问答没有长期记忆需求引入记忆层属于过度设计。如果业务对实时性要求极高但记忆检索链路里又加入了向量召回那检索耗时可能成为新的瓶颈。这时候需要权衡是直接拼接上下文还是走“先检索再注入”的流程。另外Zero-Mem 这类方案并不能解决所有 Agent 问题。它优化的是记忆的存取成本不改变模型本身的推理能力。模型理解能力弱、工具调用频繁出错、任务规划逻辑混乱这些问题不会因为记忆层改造而自动消失。2.3 合规与安全边界记忆数据往往包含真实用户信息这一点必须严肃对待。无论采用哪种记忆方案都要遵守数据最小化原则只存业务需要的数据不存无关敏感信息对存储内容做脱敏处理涉及个人信息时必须获得授权如果记忆服务要对外暴露还应该加上访问控制和审计日志。从安全和隐私角度更稳妥的做法是用户身份隔离使用独立的命名空间不同用户的记忆数据物理隔离或逻辑隔离删除用户数据时要同时删除记忆存储和备份中的对应内容。3. 环境准备与前置条件在这一类设计思路上手前先把基础环境准备好。实际部署时项目结构可能不同下面给一个通用清单。3.1 操作系统与运行时Linux 或 macOS 做服务端部署更常见Windows 也可以用。Python 3.9 以上推荐 3.10 或 3.11。Node.js 环境可选如果你要把记忆服务接入 Node 生态。3.2 依赖组件根据你的 Agent 框架选择常见的组合是Agent 框架LangChain、LlamaIndex、自研 Agent 框架或者直接使用模型的 Chat 接口。向量存储Chroma、FAISS、Milvus、Qdrant 等。结构化存储SQLite、PostgreSQL用于保存记忆条目和元数据。Embedding 模型如果走语义召回按实际业务挑选合适的 embedding API 或本地模型。Key-Value 缓存Redis常用于会话状态缓存。3.3 环境检查清单检查项说明Python 版本3.9 以上避免语法兼容问题磁盘空间记忆库和索引需要预留空间小规模测试几百 MB 起步端口占用如果要把记忆层做成 API 服务先确认端口未被占用Embedding 模型可用语义检索前先单独测试 embedding 接口是否通模型服务地址确认 LLM API 或本地推理服务地址可用3.4 项目目录建议zero_mem_demo/ ├── memory_store.py # 记忆存储实现 ├── memory_manager.py # 记忆读写与注入管理器 ├── agent_core.py # Agent 调用逻辑 ├── api_service.py # 可选的记忆服务 API ├── tests/ # 测试脚本 ├── data/ │ └── memory_store.json # 记忆落盘文件 └── requirements.txt这样拆分的好处是记忆层可以独立测试也能单独升级不影响 Agent 主逻辑。4. Zero-Mem 设计思路拆解这一节是这个设计范式的核心。要理解 Zero-Token Memory Operations先看它到底优化了什么。4.1 传统 Agent 记忆为什么贵传统做法的伪代码如下# 传统做法每次把全量历史拼进 prompt def build_prompt_with_history(user_input, history): history_text \n.join( fuser: {item[user]}\nassistant: {item[assistant]} for item in history ) return f历史记录\n{history_text}\n\n用户输入{user_input}问题很明显历史越长prompt 越大token 成本线性上升响应首字时间也在增加。4.2 Zero-Mem 的四个关键动作如果把记忆操作看成一套函数那么四个核心动作是write、read、update、delete。Zero-Mem 的思路是让这四个动作直接作用在外部记忆存储上而不是作用在“提供给模型的文本”上。write写用户输入、工具调用结果、Agent 中间推理结论清洗后直接写入记忆存储。这个过程不调用 LLM不产生 token 消耗。read读根据当前用户输入从记忆存储中召回相关条目。召回方式可以是元数据精确匹配、规则匹配或向量检索。向量检索会消耗 embedding 调用但 embedding 模型与 LLM 不是同一个推理链路不占用 LLM 上下文 token。update更新当对话推进到新阶段旧记忆可能过期或需要摘要合并直接在存储层完成更新不需要重跑模型。delete删除超过保留期限、用户要求删除或确认无用的记忆直接从存储中删除不进入模型上下文。4.3 记忆注入的时机记忆操作不消耗 token不代表注入结果不消耗 token。模型最终生成回复时仍然需要看到一部分被召回的记忆。Zero-Mem 的关键在这里控制注入的时机和数量而不是完全消除所有上下文。推荐策略是分层注入层级内容是否必选系统指令层角色、任务目标、回复规则必选会话状态层当前对话的关键状态、未完成的操作通常必选长期记忆层由检索召回的用户偏好、历史结论按需注入知识检索层RAG 检索到的文档片段按需注入每一层都设置配额上限比如长期记忆层最多注入 5 条知识检索层最多注入 3 段。超出配额的部分由记忆管理系统负责再次筛选或摘要而不是无脑塞给模型。4.4 与传统方案的区别对比项传统全量拼接滑动窗口向量 RAG 注入Zero-Mem 风格历史记录存储在 prompt 中在 prompt 中在外部存储中外部存储可选向量索引记忆写入每次拼入上下文滑动窗口内独立写入写入不消耗推理 token读取方式全量读取最近 N 轮向量召回元数据/规则/向量混合召回上下文大小随轮次增长固定取决于召回数量分层配额控制系统复杂度低低中中高从这张表可以看出来Zero-Mem 本质上是把记忆系统从“prompt 拼装问题”升级为“独立的记忆服务设计问题”。5. 零 Token 记忆操作的参考实现下面给出一套可以直接运行的参考实现重点演示“记忆读写不调用 LLM”这个核心思想。5.1 记忆存储模块from dataclasses import dataclass, asdict from typing import Any, Optional import time import json dataclass class MemoryEntry: memory_id: str content: str source: str # user / assistant / tool / system timestamp: float metadata: dict class ZeroMemStore: 外部记忆存储读写全程不调用 LLM。 def __init__(self, storage_path: str ./data/memory_store.json): self.storage_path storage_path self._entries: dict[str, MemoryEntry] {} self._load() def write(self, entry: MemoryEntry) - None: self._entries[entry.memory_id] entry self._persist() def read(self, memory_id: str) - Optional[MemoryEntry]: return self._entries.get(memory_id) def delete(self, memory_id: str) - bool: existed self._entries.pop(memory_id, None) is not None if existed: self._persist() return existed def query_by_metadata(self, **kwargs) - list[MemoryEntry]: results [] for entry in self._entries.values(): if all(entry.metadata.get(k) v for k, v in kwargs.items()): results.append(entry) return sorted(results, keylambda x: x.timestamp, reverseTrue) def query_recent(self, limit: int 10) - list[MemoryEntry]: all_items sorted( self._entries.values(), keylambda x: x.timestamp, reverseTrue ) return all_items[:limit] def _persist(self) - None: with open(self.storage_path, w, encodingutf-8) as f: json.dump( {k: asdict(v) for k, v in self._entries.items()}, f, ensure_asciiFalse, indent2 ) def _load(self) - None: # 实际实现需要处理文件不存在和 JSON 解析异常等边界情况 pass这个模块的价值在于它的所有操作都是本地 JSON 读写不涉及模型调用因此“写记忆”“删记忆”这类动作的 token 成本是零。5.2 记忆管理器class MemoryManager: def __init__(self, store: ZeroMemStore): self.store store def memorize_user_input(self, user_input: str, metadata: dict | None None) - str: memory_id fmem_{int(time.time() * 1000)} self.store.write( MemoryEntry( memory_idmemory_id, contentuser_input, sourceuser, timestamptime.time(), metadatametadata or {} ) ) return memory_id def recall_relevant( self, user_input: str, max_items: int 5, metadata_filter: dict | None None ) - list[MemoryEntry]: 先走元数据过滤再按时间倒序取最近条目。 if metadata_filter: candidates self.store.query_by_metadata(**metadata_filter) else: candidates self.store.query_recent(limitmax_items * 3) return candidates[:max_items]注意这里的recall_relevant没有做语义匹配。它的召回策略是“元数据 时间倒序”。这样的好处是召回过程完全不需要调用 Embedding也不涉及 LLM速度最快。如果需要语义召回可以在recall_relevant里加入向量检索。5.3 语义召回扩展class SemanticRecallMixin: def recall_by_semantic( self, query: str, embedding_func, index, top_k: int 5 ) - list[MemoryEntry]: query_vector embedding_func(query) hit_ids index.search(query_vector, top_k) return [self.store.read(mid) for mid in hit_ids if self.store.read(mid)]向量检索会引入额外的计算时延但它的优势是能找到关键词不匹配但语义相关的记忆。实际项目里可以先用元数据过滤缩小范围再做语义召回最后按时间重新排序。5.4 注入 promptdef build_system_prompt(user_input: str, relevant_memories: list[MemoryEntry]) - str: memory_lines [] for mem in relevant_memories: memory_lines.append(f- [{mem.source}] {mem.content}) memory_text \n.join(memory_lines) if memory_lines else 暂无相关记忆 return f你是一个使用外部记忆管理的 AI 助手。 【当前相关记忆】 {memory_text} 【用户本轮输入】 {user_input} 核心区别是这里只注入“相关记忆”而且条数可控。历史数据不会因为轮次增加而无限膨胀。6. 功能测试与效果验证一个设计再漂亮也要通过测试才能确认是否有效。建议从以下几个维度做验证。6.1 测试一多轮对话记忆保留率目的验证长对话场景下Agent 能否通过外部记忆层正确引用早前信息。过程第一轮让 Agent 记住一个事实比如“用户所在城市是杭州”。中间穿插 8 到 10 轮无关对话。最后一轮问“用户所在城市是哪里”。预期结果记忆层能在第 3 步的输入中召回对应条目。LLM 回答“杭州”而不是回答“不知道”或“没有提到”。整轮 prompt 中不包含全部历史记录只包含相关记忆片段。如果失败优先检查记忆召回是否命中了正确的 metadata而不是直接怀疑 LLM。6.2 测试二Token 消耗对比目的量化 Zero-Mem 风格记忆管理与全量拼接的 token 差异。做法准备两组相同对话数据分别用两种方式构建 prompt统计 token 数。def count_tokens(text: str) - int: # 实际项目里用模型对应 tokenizer这里只做估算 return len(text) // 3判断标准在固定轮次下全量拼接的 token 数随轮次线性增长。Zero-Mem 风格注入的 token 数保持平稳只随召回条数和 prompt 模板变化。这是最容易看到收益的测试也是这个设计范式的核心价值。6.3 测试三召回质量目的验证记忆召回是否能找到真正有用的条目。做法预置 20 条不同主题的记忆主题包括项目进度、用户偏好、会议纪要等。输入一个与“项目进度”相关的查询。检查召回结果中是否包含相关记忆并观察是否注入了大量无关内容。判断标准相关信息出现在召回列表前 5 位。无关信息占比不高没有明显干扰模型回复。如果召回质量不稳定考虑引入 embedding 向量检索或者为记忆条目增加更精准的标签体系。6.4 测试四批量记忆写入目的验证记忆层能否支撑批量场景。def batch_archive(store: ZeroMemStore, batch: list[dict]) - None: for item in batch: store.write( MemoryEntry( memory_iditem[memory_id], contentitem[content], sourceitem.get(source, system), timestampitem.get(timestamp, time.time()), metadataitem.get(metadata, {}) ) ) print(fbatch archived: {len(batch)} entries)批量写入时观察两个指标处理时间是否可控。落盘文件是否保持结构完整。7. 接口 API 与批量任务如果要把记忆层独立成服务可以按下面的方式封装。这个例子使用 FastAPI 做演示实际接口路径和参数需要按项目情况调整。7.1 简单记忆 Servicefrom fastapi import FastAPI from pydantic import BaseModel app FastAPI() store ZeroMemStore() manager MemoryManager(store) class MemoryWriteRequest(BaseModel): memory_id: str content: str source: str user metadata: dict {} class MemoryReadRequest(BaseModel): memory_id: str app.post(/memory/write) def memory_write(req: MemoryWriteRequest): entry MemoryEntry( memory_idreq.memory_id, contentreq.content, sourcereq.source, timestamptime.time(), metadatareq.metadata, ) store.write(entry) return { status: ok, memory_id: req.memory_id } app.get(/memory/read/{memory_id}) def memory_read(memory_id: str): entry store.read(memory_id) if entry is None: return {status: not_found} return asdict(entry) app.post(/memory/recall) def memory_recall(metadata_filter: dict, limit: int 5): entries store.query_by_metadata(**metadata_filter) return { status: ok, items: [asdict(e) for e in entries[:limit]] }启动服务uvicorn api_service:app --host 127.0.0.1 --port 80007.2 curl 调用示例curl -X POST http://127.0.0.1:8000/memory/write \ -H Content-Type: application/json \ -d { memory_id: mem_001, content: 用户偏好简洁回复, source: user, metadata: {scene: chat, user_id: u_123} }curl -X POST http://127.0.0.1:8000/memory/recall \ -H Content-Type: application/json \ -d {metadata_filter: {user_id: u_123}, limit: 5}7.3 Python 客户端示例import requests BASE_URL http://127.0.0.1:8000 def save_memory(memory_id: str, content: str, metadata: dict): resp requests.post( f{BASE_URL}/memory/write, json{ memory_id: memory_id, content: content, metadata: metadata }, timeout10 ) return resp.json() def recall_memories(user_id: str, limit: int 5): resp requests.post( f{BASE_URL}/memory/recall, json{metadata_filter: {user_id: user_id}, limit: limit}, timeout10 ) return resp.json()7.4 批量任务设计批量任务主要分成三类任务类型说明建议批量写入导入历史记录到记忆库分批写入每批 100 到 500 条记录失败项批量索引为已有记忆生成向量索引可放在低峰期执行失败重试批量清理清理过期或已删除用户的数据加任务队列先备份再删除批量任务建议加日志和失败重试策略避免一次性处理大量数据时因网络或磁盘问题中断。8. 资源占用与性能观察Zero-Mem 的记忆层不直接消耗 GPU 显存但在系统中仍然会影响资源占用。重点关注以下几个指标指标观察方式判断标准记忆写入耗时单独调用写入接口单条应在毫秒级落盘 JSON 文件后检查是否阻塞召回耗时元数据召回和向量召回分别计时元数据召回应远低于向量召回内存占用查看 Python 进程 RSS记忆条目越多内存占用越高需要关注膨胀落盘文件大小查看 JSON 文件大小长期运行后需要定期归档和压缩LLM 请求 token在模型服务日志中查看 usage对比接入前后单轮平均 token用命令行可以快速观察进程资源ps aux | grep python用 Python 脚本记录耗时import time start time.perf_counter() store.write(entry) elapsed time.perf_counter() - start print(fwrite elapsed: {elapsed * 1000:.2f} ms)如果显存或内存吃紧优先排查是不是向量索引加载了过多数据或者 JSON 文件读入时一次性把全量记忆载入内存。更稳妥的方案是改用 SQLite 或 PostgreSQL 做持久化按需读取而不是每次全量加载。9. 常见问题与排查方法这里整理了一些实际工程中容易遇到的问题按“现象 - 原因 - 排查 - 解决”的方式列出。问题现象可能原因排查方式解决方案记忆写入后进程崩溃JSON 文件被并发写入查看日志中的写入报错加文件锁或用数据库替代 JSON 落盘召回结果为空metadata 键名不一致打印存储中的 key 和查询条件统一 metadata 命名规范注入记忆太多prompt 变大召回条数没有限制检查recall_relevant的max_items强制配额上限超限丢弃或摘要多轮对话仍然忘记早期信息召回策略没有命中对应上下文打印每轮召回结果增加关键词索引或向量检索向量召回太慢索引加载策略不当单独测试检索接口耗时缩小索引范围或使用 ANN 索引批量任务卡住网络请求超时而无重试查看任务队列日志增加超时和重试机制记忆服务端口被占用其他进程占用端口用lsof -i:8000查看换端口或停掉冲突进程用户数据错乱没有按用户隔离检查记忆条目的 metadata 中 user_id所有查询和写入强制带用户维度模型回答里出现记忆中的错别字或错误信息记忆写入时未清洗查看写入内容是否包含乱码写入前做文本清洗和长度限制最值得注意的坑是“记忆项切分不当”。如果直接把用户一大段话当作一条记忆存储召回时往往不够精确如果切得太碎又会产生大量无关条目。通用做法是先按主题抽取比如“项目A的截止时间”“用户对B功能的偏好”这样语义更清晰。10. 最佳实践与使用建议10.1 先做最小可用版本首次落地不要直接上向量检索、分布式存储这些复杂组件。先把 JSON 存储 元数据召回跑通确认多轮对话能保留关键信息再逐步加入向量检索和索引优化。这套思路的收益主要来自“记忆操作与推理解耦”而不是某个具体存储组件。10.2 记忆分层推荐分成三层会话层保存当前会话的最近状态轮次结束可清理。长期层保存跨会话的用户偏好、项目信息、历史结论。知识层保存从外部文档中抽取的事实条目。会话层适合用 Redis 或内存缓存长期层适合用数据库知识层可以配合向量索引。分层的好处是不同数据有不同的生命周期和管理策略。10.3 注入策略要保守记忆注入不是越多越好。相关记忆超过 5 到 10 条之后对模型回复质量的边际收益会明显下降反而可能增加 token 成本。建议给每层设置明确配额并定期检查实际注入后的效果。10.4 数据合规不能省记忆数据可能包含用户个人信息。上线前务必确认是否获得用户授权。是否支持用户查询和删除自己的记忆。敏感数据是否做了脱敏。服务日志是否记录了完整的个人信息。这些不是可选项是合规底线。10.5 定期归档与压缩长期运行后记忆库会越来越大。建议定期把陈旧记忆归档到冷存储对话摘要合并可以定时执行。删除用户账号时相关记忆也要级联删除否则会持续占用存储资源也带来隐私风险。11. 总结与下一步这次我们拆解了 Zero-MemZero-Token Memory Operations for LLM Agents这个记忆管理范式。它的核心思路并不复杂把记忆的读写操作从 LLM 上下文中剥离出来让外部记忆层负责存储、检索、更新和删除模型只接收经过筛选的相关片段。建议你在自己的 Agent 项目里先做三件事用 JSON 元数据召回实现一个最小记忆层跑通“写入不调模型、读取按需注入”的流程。对比同一组对话在“全量拼接”和“Zero-Mem 风格”两种方式下的 token 消耗。确认多轮对话中的关键信息是否能被外部记忆层正确保留。最容易踩的坑是召回结果不稳定以及记忆条目的切分粒度过粗或过细。前者靠增加索引或调 metadata 设计解决后者靠主题化切分和写入前清洗解决。下一步可以扩展的方向包括接入向量数据库做语义召回、把记忆层封装为独立服务、为多 Agent 共享记忆设计权限模型以及把记忆归档和压缩做成自动化任务。这个方向的价值在于它不依赖更强的模型也不需要更大的显存而是通过架构设计把 token 预算真正用在推理上。对正在做长会话 Agent 和 RAG 应用的人来说这套思路值得收藏备用。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门