首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >用 Elasticsearch 给 AI Agent 装一颗"大脑":从零构建跨会话记忆系统

用 Elasticsearch 给 AI Agent 装一颗"大脑":从零构建跨会话记忆系统

原创
作者头像
点火三周
发布2026-04-30 15:20:04
发布2026-04-30 15:20:04
1190
举报
文章被收录于专栏:Elastic Stack专栏Elastic Stack专栏

大模型很聪明,但它没有记忆。每次对话都是一张白纸。这篇文章记录了我如何用 Elasticsearch 的原生能力,给 Agent 装上一套真正能用的长期记忆系统。


一、Agent 的"失忆症"

跟大模型聊天的人都有过这种体验:你昨天刚告诉它你喜欢用 Python,今天它又问你用什么语言。

这不是模型笨,而是 LLM 天生无状态。每次请求都是一次全新的对话,之前说过什么,它一个字都不记得。所谓的"上下文",不过是把聊天记录塞进 prompt 里假装记忆罢了。

这在闲聊场景无所谓,但如果你想让 Agent 真正"认识"一个用户——记住他的偏好、了解他的项目背景、知道上次任务做到哪了——你就必须给它一个外部记忆层

市面上的方案五花八门:有人用 Redis 存 JSON,有人用 Pinecone 做向量检索,有人干脆把所有历史对话丢进一个 txt 文件,或者全部装到大模型里。这些方案要么太粗糙,要么太割裂——你需要一个东西同时搞定存储、检索、安全、过期这四件事。

我的答案是 Elasticsearch。不是因为它时髦,而是因为它恰好把这四件事都做得很好。


二、为什么是 Elasticsearch?

先说我的核心观点:Agent 的记忆本质上是一个搜索问题,不是一个存储问题。

你想想,人类的记忆是怎么工作的?你不会把过去三十年的经历按时间顺序从头回放一遍来回答"你最喜欢吃什么"。你的大脑做的是检索——根据当前的问题,瞬间从海量经历中捞出最相关的那几条。

这恰恰是 Elasticsearch 最擅长的事。而且它不只是一个搜索引擎,它还提供了几个关键能力,让它成为 Agent 记忆层的理想选择:

需求

Elasticsearch 的回答

语义理解

semantic_text 字段 + Inference API,零配置接入嵌入模型

精确匹配

原生 BM25 全文检索

两者融合

retriever API + RRF 倒数排名融合

权限隔离

Document Level Security (DLS),查询级别的数据隔离

自动过期

Ingest Pipeline + Painless 脚本,写入时自动计算过期时间

工具编排

Agent Builder 定义 Agent,Workflows 定义可复用的工具

最后一行是重点。Elastic 近期推出的 Agent BuilderWorkflows 这两个功能,让你不用写一行应用代码,就能把上面所有能力串成一个完整的记忆系统。Agent 自己决定什么时候存、什么时候取,Elasticsearch 负责执行。


三、设计理念:记忆是日志,不是文档

在动手之前,有一个设计决策至关重要:

把每条记忆当作一条独立的日志(Append-Only),而不是一个不断被覆盖的文档。

很多人的第一反应是维护一个"用户画像"文档,每次有新信息就 Update 进去。这条路我走过,问题很多:并发冲突、信息丢失、无法追溯。

更好的做法是:每次对话结束时,AI 主动提取高价值信息,作为一条新文档写入 Elasticsearch。查询时用搜索来找最相关的记忆,而不是读取一个越来越臃肿的 JSON。

这套方案包含四个核心机制:

  1. Append-Only 提取:AI 在对话中主动识别值得记住的信息,精炼后写入
  2. 混合搜索召回:语义搜索 + 关键字匹配 + RRF 融合,确保召回最相关的记忆
  3. 权限隔离:所有读写都绑定 user_id,记忆互不可见
  4. 记忆衰减:不同类型的记忆有不同的 TTL,偏好记一年,任务进展记一周

下面一步步落地。


四、Elasticsearch 底层配置

4.1 记忆衰减流水线(Ingest Pipeline)

第一步,我们需要一个 Ingest Pipeline,让每条记忆在写入时自动计算过期时间。

思路很简单:你告诉我每条记忆要保留多少天(ttl_days),我用 Painless 脚本算出 expires_at,后续查询时只要过滤掉过期的就行。

代码语言:json
复制
PUT _ingest/pipeline/agent-memory-pipeline
{
  "description": "Calculate expires_at based on created_at and ttl_days",
  "processors": [
    {
      "set": {
        "field": "created_at",
        "value": "{{_ingest.timestamp}}"
      }
    },
    {
      "script": {
        "lang": "painless",
        "source": """
          if (ctx.ttl_days != null && ctx.created_at != null) {
            def created = ZonedDateTime.parse(ctx.created_at);
            ctx.expires_at = created.plusDays(ctx.ttl_days).toString();
          }
        """
      }
    }
  ]
}

这比用 ILM 删除整个索引要精细得多——你可以让同一个索引里的不同记忆有不同的生命周期。用户偏好("喜欢用 YAML 格式")存 365 天,任务进展("上次写到第三章")存 7 天,各管各的。

4.2 创建记忆索引

索引的 mapping 是整个方案的骨架。关键设计点是 memory_text多字段结构

代码语言:json
复制
PUT ai-agent-memory-v2
{
  "settings": {
    "default_pipeline": "agent-memory-pipeline"
  },
  "mappings": {
    "properties": {
      "user_id":          { "type": "keyword" },
      "memory_text":      { 
        "type": "text",
        "fields": {
          "semantic": {
            "type": "semantic_text",
            "inference_id": ".jina-embeddings-v5-text-small"
          }
        }
      },
      "memory_category":  { "type": "keyword" },
      "importance_score":  { "type": "integer" },
      "ttl_days":         { "type": "integer" },
      "created_at":       { "type": "date" },
      "expires_at":       { "type": "date" }
    }
  }
}

这里有一个值得展开说的细节:semantic_text 字段类型。

这是 Elasticsearch 近期引入的一个非常优雅的设计——你只需要声明一个字段是 semantic_text 并指定一个 inference_id(比如 Jina Embeddings),Elasticsearch 就会在写入时自动调用推理 API 生成向量嵌入,查询时也自动处理。你不需要自己管理 embedding pipeline,不需要维护向量维度,不需要手动调用任何 API。

memory_text 本身还是一个普通的 text 字段,支持 BM25 全文检索。一个字段,两种检索能力,这就是混合搜索的基础。

另外,注意一下 default_pipeline 的配置。


五、Agent 工具设计:用 Workflow 定义记忆操作

Elasticsearch 底层准备好了,接下来要让 Agent 能自主调用这些能力。这就是 Elastic Agent BuilderWorkflows 发挥作用的地方。

简单解释一下这两个东西的关系:

  • Agent Builder 是你定义 Agent 的地方——它的人设、它能用哪些工具、它的行为边界
  • Workflows 是你定义工具的地方——用 YAML 声明式地编排一系列操作,比如"往 ES 写一条文档"或"执行一次混合搜索"

Workflows 的精髓在于:它是声明式的,不是命令式的。 你不需要写 Python 代码来调用 ES 客户端,你只需要用 YAML 描述"我要做什么",Elasticsearch 自己执行。这意味着整个记忆系统的工具层,零应用代码

5.1 工具一:Save_Memory(写入记忆)

这个工具让 AI 在对话过程中,把识别到的高价值信息写入 Elasticsearch。

代码语言:yaml
复制
name: Save_Memory
enabled: true
description: "提取对话中的高价值信息作为一条记忆持久化。"
inputs:
  - name: user_id
    type: string
  - name: memory_text
    type: string
    description: "精炼的记忆文本,例如:'用户喜欢用 Python 写后端'"
  - name: memory_category
    type: string
    default: "preference"
  - name: ttl_days
    type: number
    description: "记忆有效期天数"
    default: 30
steps:
  - name: insert_memory
    type: elasticsearch.request
    with:
      method: POST
      path: ai-agent-memory-v2/_doc
      body:
        user_id: '{{inputs.user_id}}'
        memory_text: '{{inputs.memory_text}}'
        memory_category: '{{inputs.memory_category}}'
        ttl_days: ${{inputs.ttl_days}}

Workflows 可以在定义之后,直接测试:

定义并测试 workflow
定义并测试 workflow

通过 tools 暴露于大模型的可调用列表:

创建一个新的 tools
创建一个新的 tools
Tools可以被大模型发现并调用
Tools可以被大模型发现并调用

注意 AI 不是把原始对话存进去,而是提炼后再存。比如用户说"我之前用 Go 写过一个微服务,但现在转 Rust 了",AI 存的是:"用户目前使用 Rust 进行后端开发,此前有 Go 经验"。这个提炼过程由 Agent 的 system prompt 控制——你在 prompt 里告诉它提取规则,它就会按规则执行。

Agent 总结并记录记忆
Agent 总结并记录记忆

经过 ingest pipeline处理之后的记忆:

5.2 工具二:Recall_Memory_Hybrid(混合召回记忆)

这是整套方案的精髓。一次查询,同时完成四件事:语义理解、关键字匹配、权限隔离、过期过滤。

代码语言:yaml
复制
name: Recall_Memory_Hybrid
enabled: true
description: "通过混合搜索(语义+关键字)和 RRF 算法,精准检索历史记忆。"
inputs:
  - name: user_id
    type: string
  - name: query
    type: string
steps:
  - name: search_memory
    type: elasticsearch.request
    with:
      method: POST
      path: ai-agent-memory-v2/_search
      body:
        retriever:
          rrf:
            retrievers:
              # 策略一:语义搜索
              - standard:
                  query:
                    bool:
                      must:
                        - semantic:
                            field: memory_text.semantic
                            query: '{{inputs.query}}'
                      filter:
                        - term:
                            user_id: '{{inputs.user_id}}'
                        - bool:
                            should:
                              - range:
                                  expires_at:
                                    gte: "now"
                              - bool:
                                  must_not:
                                    exists:
                                      field: expires_at
                            minimum_should_match: 1
              
              # 策略二:关键字匹配 (BM25)
              - standard:
                  query:
                    bool:
                      must:
                        - multi_match:
                            query: '{{inputs.query}}'
                            fields: ["memory_text"]
                      filter:
                        - term:
                            user_id: '{{inputs.user_id}}'
                        - bool:
                            should:
                              - range:
                                  expires_at:
                                    gte: "now"
                              - bool:
                                  must_not:
                                    exists:
                                      field: expires_at
                            minimum_should_match: 1
            rank_window_size: 50
            rank_constant: 20

图片:Agent Builder 中 Recall_Memory_Hybrid 工具的配置界面

我来拆解一下这个查询为什么值得细看。

retriever + rrf:这是 Elasticsearch 的 Retriever API,允许你在一次请求中组合多个检索策略。RRF(Reciprocal Rank Fusion)算法不需要你手动调权重——它根据每个结果在不同检索策略中的排名自动融合打分。语义搜索找到的结果和关键字匹配找到的结果,谁在两边都排名靠前,谁就胜出。

为什么需要两种检索? 举个例子:用户问"我之前提到的那个 K8s 项目"。语义搜索能理解"K8s"和"Kubernetes"是一回事,但如果记忆里写的是"用户正在做 K8s 集群迁移",BM25 的精确匹配反而更快更准。两者互补,RRF 融合,效果远好于单一策略。

filter 部分:每个 retriever 内部都带了两层过滤——user_id 确保只看自己的记忆,expires_at 过滤掉已过期的记忆。注意那个 must_not exists 的分支:如果一条记忆没有设置过期时间(比如某些永久性事实),它也不会被误杀。

这整个查询在 Workflow 里就是一段 YAML。没有 Python,没有 Node.js,没有任何胶水代码。Elasticsearch 既是数据库,也是执行引擎。

Agent自主调用长期记忆系统
Agent自主调用长期记忆系统

六、串联起来:Agent 如何使用记忆

配置好工具后,在 Agent Builder 中把这两个工具分配给你的 Agent,再在 system prompt 里加上记忆管理的指令。

给 Agent配置上记忆系统
给 Agent配置上记忆系统

整个流程是这样的:

代码语言:bash
复制
用户:"帮我写一段 Rust 的错误处理代码"

Agent 内部流程:
  1. 调用 Recall_Memory_Hybrid(query="用户编程语言偏好")
  2. 召回记忆:"用户目前使用 Rust,偏好简洁风格"
  3. 结合记忆生成代码(知道用户喜欢简洁,就不会写一堆冗余的 match 嵌套)
  4. 对话结束前,发现新信息:"用户在做 CLI 工具开发"
  5. 调用 Save_Memory(memory_text="用户正在用 Rust 开发 CLI 工具", ttl_days=30)

关键在于:Agent 自己决定什么时候存、什么时候取。 你不需要在应用层写 if-else 来判断"这条信息值不值得记住"。LLM 本身就擅长做这种判断,你只需要在 prompt 里给它规则。


七、企业场景:权限隔离不是可选项

如果你只是给自己做一个私人助手,user_id 过滤就够了。但在企业内部,记忆隔离是硬性要求

Elasticsearch 的 Document Level Security(DLS)在这里提供了一个非常干净的方案:你可以在角色定义中直接写查询级别的过滤条件,让不同用户只能看到属于自己的文档。应用代码完全不需要关心权限逻辑——Elasticsearch 在查询执行层就把不该看的数据过滤掉了。

Elastic 官方博客中有一个很有意思的类比:美剧《人生切割术》(Severance)。主角 Mark 的大脑被植入芯片,在公司内外拥有完全隔离的两套记忆。用 Elasticsearch 的 DLS,你可以给 Agent 实现完全一样的效果——同一个 Agent,面对不同用户(或不同角色),看到的记忆完全不同,而且这个隔离是在数据层强制执行的,不是靠应用层的 if 语句。

Kibana 中配置 Document Level Security 角色的界面
Kibana 中配置 Document Level Security 角色的界面

八、回过头来看:这套方案到底好在哪

做完这套系统之后,我回头想了想它解决了什么问题:

1. 精准投喂,而不是暴力灌入

不再把几万字的历史对话塞进 prompt。每次只召回与当前问题最相关的几条记忆。这不仅省 token,更重要的是避免了 Elastic 官方博客中提到的"上下文污染"(Context Poisoning)——无关信息太多,模型反而会被带偏。

2. 越用越聪明,但也会遗忘

importance_scorettl_days 的组合让 Agent 像人一样工作:核心偏好记得牢,短期细节自然淡忘。你上周让它帮你调的那个 bug,一个月后它不会再提起;但"你喜欢简洁的代码风格"这件事,它会记一整年。

3. 零胶水代码

从 Ingest Pipeline 到索引 Mapping,从 Workflow 工具到 Agent 配置,全部在 Elasticsearch 平台内完成。没有外部服务,没有中间件,没有需要维护的 Python 脚本。Elasticsearch 既是记忆的存储层,也是记忆的计算层,还是 Agent 的执行层。

这第三点是我最想强调的。Agent Builder + Workflows 的组合,本质上是把 Elasticsearch 从一个"被调用的数据库"变成了一个"主动参与的智能体基础设施"。你不是在 Elasticsearch 旁边搭一个应用来操作它,你是在 Elasticsearch 内部定义 Agent 的行为。


九、还能做得更好

这套方案已经在跑了,但我知道它还不够完美。几个我在思考的方向:

  • 记忆合并与去重:如果用户三次说"我喜欢简洁风格",现在会存三条。理想情况下应该合并成一条并提升权重。这可以通过定期运行一个"记忆整理" Workflow 来实现。
  • 重要性衰减:一条记忆如果半年没被召回过,说明它可能不重要了。可以用 ES 的 Update By Query 定期降低长期未命中记忆的 importance_score
  • 语义记忆提炼:从大量情景记忆中蒸馏出抽象的用户画像。比如从 20 次交互中提炼出"这个用户是一个偏好实战、反感空谈的工程师"。这需要一个额外的 Agent 来做周期性的记忆压缩。

但这些都是锦上添花。核心架构已经成立:Elasticsearch 作为 Agent 的记忆层,Agent Builder 定义行为,Workflows 定义工具,混合搜索保证召回质量,Ingest Pipeline 管理生命周期。

这套东西不需要你是 Elastic 专家才能搭建。如果你有一个 Elastic Cloud 账号,从创建 Pipeline 到 Agent 跑起来,大概一个下午的时间。


写在最后

我一直觉得,AI Agent 领域最被低估的问题不是"推理能力",而是"上下文工程"。模型越来越聪明,但如果你喂给它的上下文是垃圾,输出就是垃圾。

记忆系统就是上下文工程的核心基础设施。而 Elasticsearch 恰好在这个位置上,提供了一套从存储到检索到执行的完整能力栈。

不是每个问题都需要一个新框架。有时候,答案就在你已经熟悉的工具里。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、Agent 的"失忆症"
  • 二、为什么是 Elasticsearch?
  • 三、设计理念:记忆是日志,不是文档
  • 四、Elasticsearch 底层配置
    • 4.1 记忆衰减流水线(Ingest Pipeline)
    • 4.2 创建记忆索引
  • 五、Agent 工具设计:用 Workflow 定义记忆操作
    • 5.1 工具一:Save_Memory(写入记忆)
    • 5.2 工具二:Recall_Memory_Hybrid(混合召回记忆)
  • 六、串联起来:Agent 如何使用记忆
  • 七、企业场景:权限隔离不是可选项
  • 八、回过头来看:这套方案到底好在哪
  • 九、还能做得更好
  • 写在最后
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档