跳过正文
  1. Ais/

AI Agent的记忆与技能系统:从失忆到越用越懂你

x
作者
x
熟练掌握Spring Boot、Spring Cloud等Java技术栈,专注于分布式系统设计与微服务架构。热爱技术分享,探索编程之美。
目录

AI Agent的记忆与技能系统:从失忆到越用越懂你
#

大语言模型本质上是无状态的——每次调用,模型对"你是谁"“上次聊了什么"“你喜欢什么风格"一无所知,除非这些信息被显式塞进当前的上下文窗口。这和 RAG 要解决的问题不是一回事:RAG 补的是模型"没见过的知识”,而这里要聊的是模型"没记住的经历”——同一个用户、同一个项目,换一次会话就打回原形,昨天纠正过的错误今天还会再犯一遍。

要让 Agent “越用越懂你”,就得给它一套外部记忆机制。这篇聊聊业界现在两种主流的记忆范式,以及一个更具体的案例——本文写作过程中实际在用的 Claude Code 的 Skills + Memory 系统是怎么设计的,包括它的取舍和坑。

一、为什么无状态的 LLM 需要外部记忆
#

LLM 的"记忆"完全依赖上下文窗口,这带来两层限制:

  • 会话内:上下文窗口再大也是有限资源,历史消息终究会被压缩或截断,早期的关键信息可能被后面的内容稀释掉。
  • 会话间:这是更根本的问题——默认情况下,新开一次对话,模型对之前的一切都是空白。用户上次说过"别用 mock 数据库测试",这次还得再说一遍;项目上次讨论过要在某个日期前完成的截止时间,这次也得重新提供。

外部记忆要解决的正是"会话间"这层。思路很朴素:把值得跨会话保留的信息写到磁盘或数据库里,下次对话开始前,用某种方式把相关的部分捞回上下文。分歧只在于用什么结构存、用什么方式捞——这就引出了两种主流范式。

二、两种主流记忆范式对比
#

2.1 向量检索式记忆
#

以 MemGPT(现在的 Letta)和 Mem0 为代表的这一派,思路借鉴了操作系统的分页机制:

  • 核心记忆(Core Memory):始终驻留在上下文里的一小块结构化信息,比如用户画像、当前任务的关键约束,容量有限但访问零延迟。
  • 归档记忆(Archival / Recall Memory):存在外部向量数据库里的大量历史片段,模型通过函数调用主动检索——把当前对话内容 embedding 成向量,做相似度检索,召回 Top-K 条相关记忆插入上下文。
  • 自我编辑:模型可以调用工具主动写入、修改、删除自己的记忆,理论上能持续自我进化。

Mem0 走的是更轻量的路子:从每轮对话里用 LLM 提炼出"事实"(比如"用户是数据科学家"“用户不喜欢被追问细节”),存成向量+图谱的混合结构,下次对话按需检索注入。

这一派的优点是召回不依赖精确匹配——哪怕用户换了个说法提问,语义相近的历史记忆也能被检索到;缺点是检索结果不可预测,同样的输入不一定每次都召回同一批记忆,调试和复现比较麻烦,而且提炼"事实"这一步本身依赖 LLM,容易把细节压缩丢或者提炼错。

2.2 文件/技能式记忆
#

另一派更直接:记忆就是磁盘上的结构化文件(通常是 Markdown + frontmatter),检索不靠向量相似度,靠一个显式的索引文件——一个精简的目录,列出"有哪些记忆、分别是什么主题",模型先读索引,判断哪条记忆和当前任务相关,再按需读取对应文件的全文。

这一派的核心优势是可解释、可审计、可人工编辑——记忆是纯文本文件,人可以直接打开看、直接改、放进 git 追踪变更历史,出了问题一眼就能看出模型"记错了什么",而不用像调试向量检索那样去猜为什么召回了不相关的片段。代价是召回精度依赖索引的质量——如果索引写得含糊,或者相关记忆分散在措辞完全不同的多个文件里,模型可能判断不出该读哪个。

Skills 系统是同一套思路在"能力"而不是"事实"上的延伸:与其把所有操作流程都塞进一个巨大的系统提示,不如把每个流程打包成一个独立文件(一段简短描述 + 详细步骤),模型先看到的只是一份"技能目录"(每个技能一行描述),判断当前任务和哪个技能相关,再按需加载该技能的完整内容。这也是渐进式加载(progressive disclosure)的思路——不该在每次对话里都占着上下文的东西,先不加载。

2.3 对比一览
#

维度向量检索式文件/技能式
存储结构向量数据库 + embedding结构化文本文件 + 索引
召回方式语义相似度检索显式索引匹配 + 模型判断
可解释性低——召回结果是黑盘高——人可直接读、可 diff
可编辑性需要通过接口增删改直接编辑文本文件
版本管理通常没有天然适合 git 追踪
召回精度依赖Embedding 模型质量索引和描述的写法质量
适用场景海量、非结构化、模糊召回少量、高价值、需要可审计

两者不是互斥的——不少实际系统是混合的:高价值、少量的信息(用户身份、强约束)用文件式常驻,海量、低频访问的历史用向量式按需检索。

三、实战案例:写这篇文章时正在用的记忆与技能系统
#

这篇文章本身就是一个现成的案例。写之前,Claude Code 先做了两件事,都是文件/技能式记忆的具体应用。

第一步:调用了一个叫 brainstorming 的 Skill。 系统提示里只有一份技能目录,每个技能一行描述,比如"用于创意工作之前——创建功能、搭建组件、增加新功能……在实现之前梳理用户意图、需求和设计"。判断这次任务(“找一个前沿方向写博客”)符合"创意工作"这个触发条件后,才去读取这个 Skill 的完整内容(一份几百行的流程说明:先问澄清问题、再提出几种方案、确认设计后才动笔)。整个过程中,其余没被触发的技能(调试流程、代码评审流程等)连内容都没有被加载过,不占上下文。

第二步:维护一份分层的记忆文件。 这套记忆系统把要记住的东西分成四类:

  • user:关于用户角色、偏好、知识背景的记忆,比如"用户是资深 Java/Golang 工程师,第一次接触某个新领域时需要用已知领域类比"。
  • feedback:用户明确给过的纠正或认可,比如"不要在回复末尾总结做了什么,用户看 diff 就够了"——这条记忆一旦写下,之后所有会话都会遵守,不用用户每次重复。
  • project:项目相关的、代码里推导不出来的背景,比如某个截止日期、某个决策背后的动机。
  • reference:外部系统的指针,比如"bug 追踪在 Linear 的某个项目里"。

每条记忆是一个独立的 Markdown 文件,带 frontmatter(namedescriptionmetadata.type),文件之间用 [[name]] 互相链接。所有记忆文件的一行摘要汇总进一份 MEMORY.md 索引,这份索引常驻在上下文里,但索引本身很短——真正的内容只有被判断为"相关"时才会去读对应的文件。这和前面说的技能目录是同一个模式:先加载目录,再按需加载全文

这套机制此刻正在真实运作:它记得这个博客用 Hugo + Blowfish、部署靠 deploy.sh 手动 rsync 而不是 CI,这些内容不是写在这次对话里,是上一次协作时被沉淀进项目记忆、这次对话开始前被重新读取的。

四、踩坑与设计取舍
#

记忆写太多会变成噪音。 这套系统明确排除了一类信息:能从代码、git log、git blame 里直接推导出来的东西不存进记忆——因为那些信息一旦过时就是错误信息,而代码本身永远是最新的。记忆该存的是"看代码看不出来的东西":决策背后的动机、用户没写进代码但表达过的偏好。这个边界如果不划清楚,记忆库会迅速膨胀成一堆和当前代码状态脱节的过时描述,比没有记忆更糟——模型会基于错误的旧记忆做判断。

索引层不是可选项,是必需的。 如果没有 MEMORY.md 这一层,模型要么得把所有记忆文件全读一遍(拖慢速度、占满上下文),要么得靠语义检索去猜哪条相关(回到向量检索式的老问题)。索引本身是一种权衡——它要求写记忆的时候,摘要必须写得足够具体(“一句话钩子”),太笼统的索引条目等于没有索引,模型判断不出该不该展开读。

“哪怕只有 1% 相关也必须触发"这类强规则容易过度触发。 这次写博客的过程就是个例子——为一篇博客文章走了一遍完整的"brainstorming"流程(问澄清问题、提两三个方案、等设计确认),这套流程原本是为软件工程任务设计的,套在内容创作上多少有点形式大于内容。触发规则写得越"宁可错杀不可放过”,就越容易在不完全匹配的场景里被激活,执行时就需要判断力去裁剪掉不适用的步骤(比如这次跳过了"把设计写成 spec 文档提交到 git"这一步,因为博客文章不是软件实现计划)。规则本身没法覆盖所有边界情况,需要执行时的具体判断来补足。

并行写入的冲突问题。 如果多个 Agent 实例并行运行、同时想更新同一份记忆文件,会遇到和多智能体协作里"并行写操作几乎必然冲突"同样的问题——这也是为什么记忆系统通常设计成单线程写入、由一个 Agent 在对话过程中串行地读写,而不是开放给并发写入。

五、总结
#

Agent 记忆系统的本质不是"让模型记住一切",而是可控的状态外部化——把值得跨会话保留的信息,从"隐式地依赖模型参数或对话历史"变成"显式地写在某个人和模型都能读懂、都能修改的地方"。向量检索式和文件/技能式只是这个思路下两种不同的存储和召回策略,选哪种取决于你更在乎召回的模糊容错率,还是可解释性和可审计性。

对个人博客这种规模的项目而言,文件式记忆几乎是唯一合理的选择——数据量小、更新频率低、但每一条都值得被人工检查。而这恰恰也是为什么"记忆"和"技能"值得放在一起讲:两者用的是同一套设计哲学,一个记住"发生过什么",一个记住"该怎么做",都靠一份轻量索引 + 按需加载全文来避免污染上下文。

通过邮件回复

相关文章

多智能体协作实战:什么时候该拆Agent,什么时候不该

多智能体协作实战:什么时候该拆 Agent,什么时候不该 # 这两年"多智能体"(Multi-Agent)几乎成了 AI 应用绕不开的话题——Claude Code 里的 subagent、n8n 里的多节点工作流、各种 Agent 编排框架,本质上都在回答同一个问题:一个大模型上下文搞不定的任务,能不能拆成几个更小的、各司其职的智能体来协作完成?

MCP的AI上下文管理心得

MCP的AI上下文管理心得 # 在使用 MCP(Model Context Protocol,模型上下文协议)构建 AI 应用的过程中,上下文管理是绕不开的核心话题。无论是对话系统、代码助手还是知识库问答,上下文的质量直接决定了 AI 响应的准确性和连贯性。本文结合实际使用经验,聊一聊在 MCP 体系下做好 AI 上下文管理的一些心得。

OpenClaw 使用指南:用 Skills 实现 AI 全自动操控电脑

OpenClaw 使用指南:用 Skills 实现 AI 全自动操控电脑 # 在 AI 浪潮席卷全球的今天,我们不再满足于让 AI 仅仅回答问题——我们希望 AI 能够真正帮我们干活。OpenClaw 正是这样一个框架:它让大语言模型(LLM)拥有"手脚",通过可插拔的 Skills(技能) 系统,让 AI 代理能够执行文件操作、运行命令、自动化浏览器、管理进程……几乎所有你能用鼠标键盘做的事,OpenClaw 都能让 AI 替你完成。

AI的发展与现状:从传统机器学习到MCP与Skills的新纪元

AI的发展与现状:从传统机器学习到MCP与Skills的新纪元 # 人工智能(Artificial Intelligence, AI)作为21世纪最具革命性的技术之一,正在深刻地改变着我们的生活方式、工作模式和社会结构。从早期的专家系统到如今的大语言模型,AI技术经历了多个发展阶段,而近期MCP(Model Context Protocol)和Skills的出现,更是将AI应用推向了新的高度。

RAG检索增强生成:原理、数据切片与向量化详解

RAG检索增强生成:原理、数据切片与向量化详解 # 大语言模型(LLM)虽然拥有海量的通用知识,但它的知识边界止于训练数据的截止日期,也无法访问企业内部文档、私有数据库这类"没见过"的信息。想让模型回答这些问题,通常有两条路:**微调(Fine-tuning)把新知识"炼"进参数里,或者RAG(Retrieval-Augmented Generation,检索增强生成)**在回答前先把相关资料"喂"给模型。本文重点讲后者——它的基本原理、数据切片(Chunking)与向量化(Embedding)的具体做法,以及一个经常被混淆的问题:向量化和大模型训练到底是不是一回事。

常见排序算法

常见排序算法 # 排序算法是计算机科学中最基础也是最重要的算法之一。本文介绍几种常见的排序算法,并给出 Java 实现。

TLS非对称加密流程详解:从握手到对称加密通信

TLS非对称加密流程详解:从握手到对称加密通信 # TLS(Transport Layer Security,传输层安全协议)是互联网上最广泛使用的安全协议之一,它为客户端和服务器之间的通信提供了加密、身份验证和数据完整性保护。本文将详细介绍TLS握手过程中客户端和服务端的具体操作,包括CA证书验证、密钥交换,直到最终生成AES对称加密密钥进行正常通信的完整流程。

使用Debian作为路由器:完整配置指南

使用Debian作为路由器:完整配置指南 # 在企业环境或高级家庭网络中,使用Linux系统(特别是Debian)作为路由器可以提供更强大的功能、更高的灵活性和更好的性能。本文将详细介绍如何将Debian配置为功能完整的路由器,包括DHCP服务、DNS服务、NAT配置、IPv6中继、网桥设置等核心功能。

spring三级缓存介绍

6 分钟
Spring的三级缓存介绍 Spring框架在处理单例Bean的创建和依赖注入时,使用了三级缓存机制来管理Bean的生命周期和解决潜在的循环依赖问题。这些缓存主要定义在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry类中。下面结合源码进行详细介绍。