ARTICLE DETAIL

资讯详情

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

AI编程持久记忆新思路:不用Embeddings的本地结构化方案

AI编程持久记忆新思路:不用Embeddings的本地结构化方案 如果你在真实项目里长期使用 Claude Code、Cursor 或者类似的 AI 编程 agent大概率遇到过这种场景上午刚让 agent 记住一套接口规范和项目背景下午新开一个会话它又像第一次见面一样问出一模一样的问题——“这个项目用什么数据库”“认证逻辑写在哪个文件”。你会忍不住想起一句话大模型很强但它的记性是真的差。最近技术社区出现了一个名为 Llmem 的项目展示定位很明确Local persistent memory for AI coding, no embeddings。翻译过来就是“面向 AI 编程的本地持久记忆不使用嵌入向量”。这个消息之所以值得关注不是因为它又造了一个“记忆工具”而是它提出了一个反直觉的工程取向AI 编程里的持久记忆不一定非要走向量检索那条路。这篇文章会从三个角度展开先拆解 AI coding 为什么需要持久记忆再对比“embeddings 路线”和“结构化本地记忆路线”的差异最后给出一套可落地的“类 Llmem”最小实现让你能在自己的 agent 工作流里真正试用这种思路。读完你会明白什么时候该用向量库什么时候用文件加标签就够了。1. 为什么 AI coding 需要持久记忆AI 编程 agent 的本质是一个“无状态进程”。每次新开对话它都从空上下文开始只能依赖你粘贴到 prompt 里的内容、当前打开的文件以及工具自带的工作区索引。这导致一个很尴尬的现象单次对话内 agent 表现很好跨会话之后它就“失忆”。具体到开发场景痛点有三个。第一个是跨会话的上下文断裂。上午你带着 agent 梳理完订单模块的表结构下午想让它继续写接口它完全不记得上午的结论。你只能重新粘贴一遍或者不断提醒。这种重复劳动在大型项目里会消耗大量时间。第二个是多 agent 协作时的信息隔离。当前不少团队开始尝试用多个 agent 分别处理前端、后端、测试、文档。问题是负责后端设计的 agent 产出的决策负责前端联调的 agent 完全不知道。agent 之间没有共享记忆协作变成了一场“各说各话”。第三个是长周期项目的知识沉淀。一个项目跑三个月后真正有价值的信息往往散落在几十次 AI 会话里某处绕过了一个坑、某个模块的选型原因、某个接口的兼容性约束。这些信息没有被结构化保存等于每次都让 agent 重新“踩坑”。很多人对这个问题给出的标准解法是“上 RAG”“上向量库”。但向量检索解决的是“模糊语义召回”而 AI 编程场景里的大量记忆需求其实是“精确事实”接口路径是什么、数据库用的哪个版本、这个模块之前为什么这么设计。用向量库去解决这类问题一方面复杂度上来了另一方面检索结果还是黑盒你不知道 agent 到底召回了一段什么内容。这才是 Llmem 这类方案切入的点记忆问题不必等价于向量检索问题。2. Llmem 是什么Llmem 从名字就能看出它的定位Local persistent memory强调本地和持久。最关键的是最后那个限定语——no embeddings。所谓 embeddings就是嵌入向量。在 AI 应用里它通常指把文本映射成高维向量再用相似度计算来“语义匹配”。比如你把项目文档拆成很多块每一块转成一个向量用户提问时也转成向量然后通过向量相似度把最相关的几个块找出来喂给大模型。这是当前很多“AI 记忆”产品的底层方案。Llmem 的取向是不依赖这种语义向量机制。它的记忆更接近结构化数据用可读的、可编辑的本地文件保存记忆条目通过标签、分类、关键词、路径等方式检索然后把筛选出的记忆以普通文本形式注入到 AI coding 工具的上下文中。这种设计有几个直接好处。第一是隐私性。记忆完全落在本地不需要把项目文档发送到外部 embedding API。对于很多企业内部项目、未公开项目、或客户源码这是硬性要求。第二是可解释性。向量检索是一个“黑盒”你很难知道 agent 为什么把某段内容当作相关记忆。结构化记忆则完全透明——每个条目存了什么、打了什么标签、什么时候写入的一清二楚。第三是低成本。不需要额外部署向量数据库不需要调用 embedding 模型不需要维护 embedding pipeline。一个目录加几个 JSON 文件就能跑起来。当然我在这里要说明一个事实边界由于项目目前展示的信息有限我无法列出 Llmem 官方源码中的具体 API 和内部实现。下面的内容更多是基于它的定位推导出的通用设计思路以及你可以在自己项目里复刻的“类 Llmem”方案。这一点请先明确。3. 两条记忆技术路线对比既然 Llmem 的核心卖点是“no embeddings”就有必要把两条技术路线放到一起看。先说 embeddings 路线的完整链路。通常是这样把项目文档、代码说明、会话记录等拆成小块调用 embedding 模型把每块转成向量存入向量数据库用户提问时把问题转成向量做相似度检索取 top-k 结果拼接进 prompt。这条路线强在语义理解。即使用户问法跟文档原文差异很大也能通过向量相似度找到相关内容。它适合处理大量非结构化文档比如技术wiki、需求文档、工单记录。但它的问题也很明显“相关”的判定比较模糊可能召回到语义相近但实际无效的内容部署复杂度高embedding 模型单独收费对本地部署和隐私敏感场景不友好。更关键的是在 AI 编程场景里很多记忆是“事实型”的不是“语义型”的向量化反而绕了远路。再看 Llmem 这种结构化本地路线制定记忆条目规范每个条目包含内容、标签、分类、时间、来源以 JSON、YAML、Markdown 文件形式保存在本地目录通过标签精确检索、关键词全文检索、目录层级定位来找记忆把找到的记忆拼成上下文块注入 agent 的 system prompt 或单独作为输入文件。这条路线胜在简单、透明、可控。它不需要 embedding pipeline也不依赖第三方服务。代价是它的语义能力弱如果你问的是“我记得之前讨论过那个类似 Redis 的方案叫什么”而记忆里没有“Redis”这个词只有“缓存中间件选型”这类描述关键词检索可能就找不到了。维度embeddings 路线结构化本地路线Llmem 类存储载体向量数据库本地 JSON/YAML/Markdown 文件检索方式向量相似度标签、关键词、路径结构化检索语义理解能力强能处理模糊查询弱依赖关键词和人工打标可解释性低黑盒召回高每条记忆可读可审隐私与本地化依赖 embedding 服务或本地模型完全本地无外部调用部署复杂度高需要向量库和索引管线低一个目录即可成本embedding 调用费 向量库资源几乎为零适用场景海量非结构化知识召回项目事实、决策记录、团队规范结论是这两条路线不是替代关系而是解决不同层面的问题。Llmem 这类方案更适合“项目级事实记忆”而向量检索更适合“企业级知识库”。选型之前先想清楚你要记的是“事实”还是“知识”。4. 不使用 embeddings 的记忆系统怎么设计如果你认同“AI 编程记忆可以不用向量库”这个判断下一步就是设计一个可运行的本地记忆系统。从 Llmem 的定位出发这套系统要回答五个设计问题。第一个问题记忆的最小单位是什么。一条记忆应该是一个可以单独寻址的条目而不是一段混在日志里的文字。我建议每条记忆包含以下字段id、内容、标签列表、分类、创建时间、来源会话或任务、关联模块。标签是最关键的检索入口分类则用来做粗粒度过滤。第二个问题用哪种存储格式。JSON 适合做底层存储结构清晰、解析简单Markdown 适合做人类可读的摘要和展示。一个比较实用的做法是完整记录存成 JSON 文件放在 memories 目录同时在 index.json 里维护一份轻量索引。检索时先查索引过滤再按需读完整内容。如果记忆量特别大可以考虑换成 SQLite用 FTS5 全文索引替代逐文件扫描。第三个问题如何检索。不使用 embeddings 之后可用的检索手段主要有三种标签精确匹配、关键词全文匹配、分类与时间过滤。标签检索适合高频稳定的分类关键词检索适合临时查找分类和时间过滤适合做“最近一周的决策记录”这类查询。三种方式可以组合但要注意不要把检索逻辑搞得太复杂否则就失去了本地方案的简洁优势。第四个问题如何注入到 AI 工具里。记忆系统本身不会自动产生价值关键是让 AI coding agent 在每次会话开始时读取相关记忆。通常做法是写一个脚本把筛选出的记忆拼接成固定格式的文本块agent 要么通过 system prompt 接收它要么把它作为一个待读取文件放进工作区。这一步决定了记忆能不能真正被用到。第五个问题如何写入新记忆。写入不能靠人工拷贝粘贴那样很快会退化。更好的做法是定义一套命令或脚本让 agent 在完成重要决策、部署、修复后自动调用。也就是说agent 既消费记忆也生产记忆形成一个闭环。这套设计有一个核心原则记忆要小、要准、要人类可读。冗余的记忆会挤占上下文窗口不准确的记忆会误导 agent不可读的记忆则无法被人工审核和修正。5. 最小示例给 AI coding 加一层“类 Llmem”本地记忆下面我给出一个最小可运行示例用来演示上面这套设计。再次说明这是“类 Llmem 思路”的通用实现不是 Llmem 官方源码。你需要根据实际项目调整目录和命令。5.1 记忆存储层文件路径memory_store.pyimport json import sys import uuid from datetime import datetime from pathlib import Path class MemoryStore: def __init__(self, base_dir: Path): self.base_dir base_dir self.memories_dir base_dir / memories self.memories_dir.mkdir(parentsTrue, exist_okTrue) self.index_path base_dir / index.json self._load_index() def _load_index(self): if self.index_path.exists(): self.index json.loads(self.index_path.read_text(encodingutf-8)) else: self.index {items: []} self._save_index() def _save_index(self): self.index_path.write_text( json.dumps(self.index, ensure_asciiFalse, indent2), encodingutf-8, ) def add_memory(self, content: str, tags: list, category: str): item { id: uuid.uuid4().hex[:8], content: content, tags: tags, category: category, created_at: datetime.now().isoformat(timespecseconds), } file_name f{item[id]}.json (self.memories_dir / file_name).write_text( json.dumps(item, ensure_asciiFalse, indent2), encodingutf-8, ) self.index[items].append( { id: item[id], tags: tags, category: category, file: file_name, created_at: item[created_at], } ) self._save_index() return item[id] def search_by_tag(self, tag: str): return [ item for item in self.index[items] if tag in item[tags] ] def search_by_keyword(self, keyword: str): result [] for item in self.index[items]: file_path self.memories_dir / item[file] content json.loads(file_path.read_text(encodingutf-8))[content] if keyword.lower() in content.lower(): result.append(item) return result逻辑说明MemoryStore 类负责记忆的写入和索引维护。add_memory 把完整内容写入一个独立的 JSON 文件同时更新 index.json 中的轻量元数据。search_by_tag 走索引过滤search_by_keyword 则逐个读取文件做关键词匹配。这里用逐文件扫描只是为了演示记忆量大了以后建议换成 SQLite。5.2 命令行入口文件路径cli.pyimport json import sys from pathlib import Path from memory_store import MemoryStore def build_context(memories, store): lines [[Local Memory Context], ] for i, mem in enumerate(memories, 1): file_path store.memories_dir / mem[file] data json.loads(file_path.read_text(encodingutf-8)) lines.append(f{i}. [{data[category]}] {data[content]}) lines.append(f tags: {, .join(data[tags])}) lines.append() return \n.join(lines) def main(): store MemoryStore(Path(.)) if len(sys.argv) 2: print(用法: python cli.py add|recall [参数]) sys.exit(1) command sys.argv[1] if command add: content sys.argv[sys.argv.index(--content) 1] tags sys.argv[sys.argv.index(--tags) 1].split(,) category sys.argv[sys.argv.index(--category) 1] memory_id store.add_memory(content, tags, category) print(f已保存记忆ID: {memory_id}) elif command recall: if --tag in sys.argv: tag sys.argv[sys.argv.index(--tag) 1] memories store.search_by_tag(tag) elif --keyword in sys.argv: keyword sys.argv[sys.argv.index(--keyword) 1] memories store.search_by_keyword(keyword) else: memories store.index[items] print(build_context(memories, store)) else: print(f未知命令: {command}) sys.exit(1) if __name__ __main__: main()说明cli.py 提供 add 和 recall 两个命令。add 用于向记忆库写入新条目recall 用于按标签或关键词召回记忆并输出格式化的上下文块。这样做的好处是AI agent 可以像调用一个普通命令一样读取和写入记忆。5.3 与 AI coding 工具集成记忆系统建好之后要让 agent 真正用起来。以 Cursor 或 Claude Code 这类工具为例项目配置文件中可以加入以下说明文件路径CLAUDE.md 或 .cursorrules按项目实际工具选择## 记忆使用规范 - 开始新任务前先执行 python cli.py recall --tagproject-facts获取项目背景记忆。 - 如果记忆不足允许使用 python cli.py recall --keyword关键词 做补充检索。 - 完成重大技术决策、架构调整、接口修改后执行 python cli.py add --content决策内容 --tagsdecision,模块名 --categorydesign 保存记忆。 - 保存记忆时避免写入密钥、密码、token 等敏感信息。集成要点是记忆的读写被定义成了 agent 工作流的一部分而不是事后的手工归档。agent 开始任务前“回忆”结束任务后“记录”这比任何自动 embedding 方案都更容易被团队理解和维护。5.4 运行与验证先在项目目录里写两条记忆python cli.py add \ --content订单服务使用 PostgreSQL接口路径前缀为 /api/v1/orders \ --tagsproject-facts,order \ --categoryarchitecturepython cli.py add \ --content登录认证使用 JWT过期时间为 2 小时刷新逻辑在 auth_service.py \ --tagsproject-facts,auth \ --categoryarchitecture再执行召回python cli.py recall --tagproject-facts预期输出大致是[Local Memory Context] 1. [architecture] 订单服务使用 PostgreSQL接口路径前缀为 /api/v1/orders tags: project-facts, order 2. [architecture] 登录认证使用 JWT过期时间为 2 小时刷新逻辑在 auth_service.py tags: project-facts, auth这段输出可以直接被粘贴到 agent 的对话上下文里。在自动化集成中也可以用命令替换和重定向把输出写入一个 context.md 文件再让 agent 读取该文件。6. 适用场景与边界Llmem 这种“本地结构化记忆”思路并不是所有场景的银弹。结合前面的对比我把适用场景和边界整理清楚。适合采用的场景首先是隐私敏感或离线开发环境。项目代码不能离开本机、不能调用外部 API 时结构化本地记忆几乎是唯一可行的记忆方案。其次是中小型项目记忆条目的数量级在几十到几百条完全不需要引入向量库。再次是需要记忆可审计的团队。每条记忆都可以被人工 review错误记忆可以直接修改文件而不是在黑盒向量库里“找逻辑”。还有一类场景是 AI 编程工具本身工作区不稳定的情况。有些 agent 插件的工作区索引会因为 IDE 升级、缓存清理而失效但项目目录下的记忆文件是跟随代码仓库走的只要 git 在记忆就在。如果再配合团队仓库共享记忆目录就相当于给 agent 建立了一份“团队级项目记忆”。不适合采用的场景也很明显。如果你的需求是从几万篇非结构化文档里做语义问答那关键词和标签检索几乎无法工作这种场景还是老老实实上 embeddings。另外如果记忆内容需要跨语言的语义关联比如用户用中文问一条英文文档里的知识结构化记忆也会力不从心。还有一个现实边界结构化记忆依赖“打标”。如果团队成员没有良好习惯标签渐渐失序记忆库就会慢慢变成垃圾堆。我的建议是标签体系在一开始就要定义清楚比如固定几类project-facts项目事实、decision决策记录、pitfall已知坑、api-doc接口说明。宁可用固定枚举也不要让 agent 自由发明标签。7. 常见问题与排查建议下面按照这套示例实现整理几个常见问题和排查思路。问题现象可能原因排查方式解决方案召回结果总是为空标签拼写不一致或检索目录错误查看 index.json 里的标签值检查当前工作目录统一标签命名规范在 recall 前打印 store.base_dir 确认路径agent 没有使用记忆记忆上下文没有被注入到 prompt检查配置文件中是否真的执行了 recall 命令把记忆输出重定向到 context.md并让 agent 强制读取该文件记忆数量多了以后召回变慢逐文件读取内容做关键词匹配估算 memories 目录文件数量换成 SQLite FTS5 全文索引或按分类拆分子目录同时运行多个 agent 时索引被写坏并发写入 index.json检查 index.json 是否出现了 JSON 解析错误在写入时加文件锁或改用 SQLite 的事务机制记忆里包含过时信息agent 被误导没有清理过期记忆按 created_at 排序人工 review 旧条目增加定期清理机制为记忆增加 TTL 或状态字段这里要特别提醒一个原则任何记忆系统都会产生“坏记忆”。不要假设 agent 写入的记忆一定是正确的。更稳妥的做法是让 agent 写完记忆后把记忆内容打印到控制台由人工确认后再写入。尤其是多 agent 协作时一个 agent 的错误结论如果被另一个 agent 当成事实参考错误会被不断放大。8. 最佳实践与工程建议如果决定在真实项目中采用这套本地记忆方案以下建议值得参考。第一记忆目录不要散落在项目外。推荐把记忆目录放进代码仓库用 git 管理。这样每次提交都能看到记忆的变化review 代码的同时也能 review 记忆。出了问题也可以随时回滚。第二标签体系要克制。我见过不少团队把标签设计成自由填写的文本框结果两周后出现了几十种语义重叠的标签。建议定义一个可枚举的标签集合agent 只能从中选择。比如参考这个集合project-facts、architecture、decision、pitfall、api-doc、todo。第三写入和读取要权限分离。理想的情况下agent 有自读自写的权限但关键类别的记忆应该设置为“只读”或“需确认”。比如 architecture 和 decision 这类长期记忆一旦写错影响很大而 todo 这种临时记忆可以放权让 agent 自由写。第四上下文注入要控制量。系统提示词的上下文窗口始终是稀缺资源。建议默认只召回最相关的一小部分记忆不要一次性把所有记忆全部灌入。同时可以在 recall 命令里增加 --limit 参数限制返回条数并优先返回最新创建的条目。第五记忆中禁止存储敏感信息。不要把它当作密码本。密钥、数据库连接串、内网地址、客户个人信息都不应该进入记忆文件。如果项目确实需要让 agent 知道敏感配置应该通过环境变量或专门的密钥管理工具注入而不是写进项目内的记忆库。第六操作生产环境或写关键决策记忆前尽量在本地测试环境验证一遍。如果是团队共用记忆目录建议通过 Pull Request 而不是直接 push 来修改记忆文件。理由很简单记忆是 agent 的行为依据改动记忆就等于改动 agent 的行为理应走变更评审流程。9. 总结Llmem 这个项目值得关注不是因为“本地记忆”这个概念有多新而是它提供了一个清醒的判断AI 编程的持久记忆不等于向量数据库不等于 embeddings更不等于复杂的基础设施。在很多实际场景里一个结构化的本地目录、一套标签规范、一个简单的召回脚本就能解决大部分问题。从工程成本看这类方案最难得的地方是“朴素”。它把记忆从黑盒变成了白盒从外部服务变成了本地资产从高昂成本变成了零成本。这种思路对于中小团队、隐私敏感项目和个人开发者来说尤其有价值。如果你接下来想动手实践我建议先做两件事第一按上面的代码在本地项目里跑通 add 和 recall 两条链路验证记忆注入是否真的改变了 agent 的行为第二观察两周看哪些记忆类别是高频使用的再根据真实需求去迭代标签体系和检索方式。等你把这些跑熟了再回头比较“embeddings 路线”和“结构化路线”你会有自己的答案。
返回列表