Elastic Atlas Agent Memory:不写存储,Agent 记忆

Agent 记忆这个话题,圈子里已经讨论了大半年了。从 MemGPT 到 GraphRAG,市面上不缺方案,但大部分要么太简单(只存原始对话),要么太重(要搭图数据库)。

上周我看到 Elastic 开源了一个新项目------Atlas Agent Memory。它走了一条中间路线:基于 Elasticsearch 做三类记忆分离,通过 MCP 直接对接任何 Agent。我花了一个周末把它跑起来接入了 Claude Desktop,今天分享一下上手体验。

Agent 不能只靠上下文窗口

先说一个共识:用 LLM 的上下文窗口存记忆是不靠谱的。

拆成三点来讲:

  1. 上下文窗口有限:4k / 16k / 100k,终究是个固定数。Agent 跑了几天,对话记录可以轻松填满。
  2. 未结构化的记忆不好用:所有历史消息堆在一起,你想让 Agent 回忆"用户上周四提到的技术栈偏好",它得从头扫一遍。
  3. 没有写入可见性:这一轮对话存进去的信息,下一轮才能看到。Agent 自己写进去的内容,在同一轮对话里看不到------这会导致它重复问相同的问题。

Atlas 解决这些问题的思路来自认知科学:把记忆分成三类,分别存储、分别检索。

三类记忆架构:情景、语义、程序性

Atlas 用了三个独立的 Elasticsearch 索引,分别对应三类记忆:

bash 复制代码
atlas_memory_episodic   -- 情景记忆:具体事件的时间线
atlas_memory_semantic   -- 语义记忆:提炼出来的事实
atlas_memory_procedural -- 程序性记忆:怎么做某件事的步骤

情景记忆是最原始的输入。每次用户发来一条消息,Atlas 就写一条情景记录,包含 timestamp、原始消息内容、还有谁发的。这个索引写入频率很高,所以它带了时间衰减逻辑------半年前的情景,权重会逐年降低。

语义记忆是 LLM 在 Consolidation 阶段从情景里提炼出来的事实。比如用户几次提到"我在用 Next.js 14",LLM 会把它提炼成一条语义事实:"用户的技术栈是 Next.js 14",并附上证据情景的 ID。下次 Agent 再问"用什么框架",直接去语义索引检索,不用翻历史消息。

程序性记忆存的是操作指南。比如 Agent 学会了"用户公司的部署流程是:先跑 linter,然后构建,然后发 Slack 通知",这个流程会被存成程序性记忆,之后类似场景可以直接复用。

混合检索管线:BM25 → Dense → RRF → Reranker

单靠语义检索是不够的。实际场景里,用户可能问"我上周说过的那个框架",这是一个精确短语匹配("上周"+"框架")------BM25 擅长;也可能问"你觉得 Go 和 Rust 哪个更适合写 CLI?"------密集向量检索擅长。

Atlas 的检索管线是四段式:

bash 复制代码
输入查询
  ↓
BM25 精确检索(lexical)
  ↓
Jina v5 密集向量检索(dense)
  ↓
RRF 融合(rank_constant=30)
  ↓
Jina v2 Cross-encoder 重排序
  ↓
Top-10 结果输出

BM25 做精确匹配,Jina v5 做语义匹配,RRF 把两个排序列表融合起来(rank_constant=30 是默认值,控制融合的平滑度),最后用 Cross-encoder 做一次精排。

官方的评测数据:R@10(检索前 10 条的回召率)达到了 0.89 。更重要的是 零跨租户泄漏------A 用户搜不到 B 用户的记忆。

5 步上手:从克隆到 MCP 接入 Claude Desktop

我们直接上手。需要先准备好:

  • Elasticsearch Cluster(8.15+)
  • Node.js 18+
  • uv(Python 包管理器)
  • Elastic Inference Service(EIS),用于 Jina embedding + Claude chat-completion

第一步,把项目克隆下来:

bash 复制代码
git clone https://github.com/noamschwartz/atlas-memory-demo.git
cd atlas-memory-demo

第二步,配置环境变量。项目提供了一个 .env.example 文件,需要填 EIS 的 endpoint 和 API key,以及 ES 集群的连接信息。关键配置项:

bash 复制代码
ELASTICSEARCH_URL=https://your-cluster.es.us-east-1.aws.cloud.es.io:9243
ELASTICSEARCH_API_KEY=your-api-key
EIS_ENDPOINT=https://your-eis-endpoint
EIS_API_KEY=your-eis-key

第三步,一行命令启动:

bash 复制代码
./setup.sh

这个脚本做了三件事:

  1. uv 创建 Python 虚拟环境并安装依赖(FastAPI + elasticsearch-py)
  2. npm install 安装前端依赖
  3. 自动创建三个 ES 索引并写入 mapping

我建议先手动确认索引已经创建成功:

bash 复制代码
# 查看三个记忆索引的 mapping
curl -X GET "https://your-cluster.es.us-east-1.aws.cloud.es.io:9243/atlas_memory_episodic/_mapping" \
  -H "Authorization: ApiKey your-api-key"

第四步,写入一条记忆:

python 复制代码
import requests

# 写入情景记忆
response = requests.post(
    "http://localhost:8000/api/atlas/write_memory",
    json={
        "memory_type": "episodic",
        "text": "用户说:我在用Next.js 14做全栈开发,觉得App Router比Pages Router好用",
        "user_id": "user_001",
        "refresh": True  # 同轮立即可见
    },
    params={"user_id": "user_001"}
)
print(response.json())

注意这个 refresh=True。默认情况下,ES 写入后需要 1 秒的刷新间隔才能被搜索到。如果 Agent 在写入后立即搜索,可能搜不到自己刚写的内容。Atlas 在写 API 里暴露了这个参数,确保同轮可见。

第五步,检索记忆。Atlas 的搜索默认返回 top-10 结果,使用完整的混合检索管线:

python 复制代码
response = requests.post(
    "http://localhost:8000/api/atlas/recall_memory",
    json={
        "query": "用户用什么框架开发",
        "user_id": "user_001"
    },
    params={"user_id": "user_001"}
)
results = response.json()
for r in results["memories"]:
    print(f"[{r['memory_type']}] {r['text']} (score: {r['score']:.3f})")

输出的结果应该会包含刚才写入的情景记录,甚至可能在 Consolidation 后返回提炼出的语义事实。

MCP 接入 Claude Desktop------这才是杀手锏

Atlas 的价值不只是"一个 Agent 记忆的 API",它提供了 MCP 端点,可以让任何支持 MCP 的 Agent 即插即用。Claude Desktop 从今年年初就开始支持 MCP 协议了。

在 Claude Desktop 的配置文件中添加 Atlas 的 MCP 端点:

json 复制代码
{
  "mcpServers": [
    {
      "name": "atlas-memory",
      "url": "http://localhost:8000/api/atlas/mcp/user_001",
      "tools": ["recall_memory", "write_memory", "forget_memory"]
    }
  ]
}

重启 Claude Desktop,Agent 就可以:

  • 用户在对话里说"帮我把之前那个项目信息调出来",Agent 调用 recall_memory 检索语义记忆
  • Agent 发现自己不知道"项目部署流程",可以调用 write_memory 写入一条程序性记忆
  • 用户纠正"我上次说的不对,应该用 pnpm 不是 yarn",Agent 调用 write_memory,同时触发 Supersession 机制标记旧记录为已替代

Atlas 提供三个 MCP 工具:

工具名 参数 作用
recall_memory query, user_id, include_superseded, include_catalog 检索记忆,默认 top-10
write_memory memory_type, text, fact_type, confidence 写入任意类型记忆
forget_memory memory_id 删除指定记忆

接入的实际效果:我的 Agent 从一个"每轮对话都忘了自己是谁"的水平,变成了可以连续聊 50 轮还能准确引用用户两天前说过的话。不是说它变成了 AGI------但它再也不会问出"你刚才说的那个部署流程能再说一遍吗"这种让用户崩溃的问题了。

关键特性:Supersession / 时间衰减 / DLS

Supersession------记忆版 Git

用户说"等等,上次我说的话不对"------这是 Agent 记忆里最常见的场景。

如果只是简单覆盖,你会丢掉原始记录信息。Atlas 的做法是:当新的记忆写入时,如果 LLM 判断它是对旧记录的纠正,就设置旧记录的 superseded_by 字段指向新记录的 ID。检索时默认不返回已被 supersede 的记录,但如果你打开 include_superseded=true,可以看到完整的审计链。

这比"覆盖"更合理:至少你知道 Agent 的"认知"是怎么演变的,而不是突然就变了。

时间衰减------Gauss 曲线 Painless 脚本

场景记忆里的记录,半年后还有多大意义?Atlas 在检索时用了 Painless 脚本计算时间衰减:

bash 复制代码
script_score = gauss(timestamp, offset=180d, scale=1825d)
  • 前 180 天的记录权重不变(offset=180d)
  • 之后的权重按 Gauss 曲线衰减,半衰期约 5 年(scale=1825d)

同时,被频繁检索过的记忆会有 use-count 加成:1 + log10(1 + use_count) * 0.2

DLS 多租户隔离

SaaS 形态的 Agent 记忆系统,最怕的是 A 用户的记忆泄漏到 B 用户那里。Atlas 用的是 Elasticsearch 原生的 Document-Level Security(DLS):

  • 每个用户一个 API Key,Key 的 role descriptor 内嵌了 user_id == X 的 term filter
  • App 层再套一层 term filter 兜底
  • 官方评测:零泄漏

如果你的 Agent 要做多租户(比如公司内部多个团队用同一个 Agent),DLS 是 Atlas 最值得关注的能力之一。

评测与性能

Atlas 的官方门控标准:

指标 门控值 实测值
R@10 ≥ 0.85 0.89
R@5 ≥ 0.75 已有数据待补充
泄漏数 = 0 0

评测采用 QA-style passage retrieval:LLM 为每篇文档写 2 个问题,然后看检索系统能不能找到对应的文档。这是一个比较标准的召回评测范式。

不过要注意,Atlas 目前只是一个 demo 项目(MIT 协议),不是正式产品。官方的 GitHub repo 也只提供了快速启动的 demo 代码,不是生产就绪的部署方案。如果你要在生产环境用,至少需要自己处理:

  • 高可用部署(目前 demo 是单实例 FastAPI)
  • 监控与告警(无内置 metrics)
  • 数据备份策略(需要自己配置 ES 快照)

什么时候值得用 Atlas

写到最后,给一个实用的决策参考:

适合用 Atlas 的场景:

  • 你的 Agent 需要做多轮跨会话的长期记忆(不只是当前对话窗口)
  • 你已经有 Elasticsearch 基础设施,或者愿意部署 ES 集群
  • 你需要多租户隔离(SaaS 产品形态)
  • 你想通过 MCP 快速接入 Claude Desktop / Cursor / Claude Code

不太适合的场景:

  • 只是简单的对话历史存储(用 Redis 或 SQLite 就够了)
  • 对延迟极度敏感(混合检索管线有 4 步,latency 比单一向量检索高不少)
  • 需要极低维护成本(你不想管 ES 集群)

Atlas 不是万能的记忆方案,它解决了一个很具体的问题------Agent 需要结构化的、可检索的、多租户隔离的长期记忆。如果你正在被"Agent 记不住东西"这个问题困扰,它至少值得跑起来看一眼。

相比之下,与其花时间调 Prompt 让 Agent "记住用户说过的话",不如把存储层做对。Atlas 给出的思路是:记忆不是一个 Chat History,它应该是一个认知系统的三层结构。这个抽象层,才是 Agent 能长期工作的关键。

不是所有 Agent 都需要记忆,但需要长期交互的 Agent,没有一个好的记忆层是活不下去的。真正的 Agent 记忆不是一个数据库,而是一个认知系统------而 Atlas 证明了至少在架构上,ES 能把这件事做对。

相关推荐
小折耳猫_2 小时前
AI 智能体可观测性
大模型·agent·智能体
树獭叔叔4 小时前
从 Search 到 Extract:AI Agent 网页工具架构拆解
后端·agent·ai编程
leeyi5 小时前
Go 项目怎么组织:DDD 4 层 vs MVC vs 脚本式
agent·ai编程·领域驱动设计
maynormoe5 小时前
从单轮 Prompt 到 Agent:三种构建范式的工程复盘
agent
老梁agent5 小时前
Agent 可观测性实战:全链路追踪 + JSON 结构化日志
物联网·agent
想ai抽5 小时前
Hermes Agent vs Loop Agent 技术调研
agent·loop·harness
用户2930750976696 小时前
手写一个 AI 编程助手:Tool Calling 与 ReAct Agent 全解
agent
千帆_Evan6 小时前
🤖 AI 写代码总在重复造轮子?我们把 Utils 复用门禁做进了 Agent 运行时(已开源)
前端·agent
ckjoker6 小时前
23个测试全绿,一个Token没花——LLM应用的单元测试该怎么写?
单元测试·agent