Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

会记忆的 Agent:介绍 Agent Memory

原文:Agents that remember: introducing Agent Memory Source: https://blog.cloudflare.com/introducing-agent-memory/

2026-04-17

随着开发者在 Cloudflare 上构建越来越复杂的 agent,他们面临的最大挑战之一是在合适的时间将正确的信息放入上下文中。模型产出的结果质量直接与它们运行时的上下文质量挂钩,但即使上下文窗口大小已经超过了一百万(1M)token,context rot 仍然是一个未解决的问题。在两个糟糕的选项之间出现了天然的张力:把所有东西都放在上下文里,然后看着质量下降;或者激进地裁剪,冒着丢失 agent 之后需要的信息的风险。

今天,我们宣布 Agent Memory 进入 private beta,这是一个托管服务,可以从 agent 对话中提取信息,并在需要时提供它们,而不会塞满上下文窗口。

它给 AI agent 提供持久记忆,让它们能够记住重要的事情、忘掉不重要的事情,并随着时间变得更聪明。在这篇文章中,我们会解释它是如何工作的——以及它能帮你构建什么。

Agentic memory 的现状

Agentic memory 是 AI 基础设施中发展最快的领域之一,新的开源库、托管服务和研究原型几乎每周都会推出。这些产品在存储什么、如何检索以及为何种 agent 设计上差异很大。像 LongMemEvalLoCoMoBEAM 这样的 benchmark 提供了有用的同类对比,但它们也让构建针对特定 evaluation 过拟合、在生产环境中失效的系统变得容易。

现有的产品在架构上也各不相同。一些是托管服务,在后台处理抽取和检索;另一些是自托管框架,你自己运行内存管线。一些暴露了受约束的、为目的而设计的 API,把内存逻辑保留在 agent 主上下文之外;另一些则给模型提供对数据库或文件系统的原始访问,让它自己设计查询,把 token 烧在存储和检索策略上而不是实际任务上。一些试图把所有东西塞进上下文窗口,如果需要的话在多个 agent 之间切分;另一些则用检索来只浮出相关的部分。

Agent Memory 是一个有明确观点的 API 和基于检索的架构的托管服务。我们仔细考虑过其他方案,我们相信这种组合是大多数生产工作负载的正确默认选择。比起给 agent 原始文件系统访问权,更紧凑的摄取和检索管线更优。除了改进的成本和性能之外,它们还为生产中所需的复杂推理任务提供了更好的基础,例如时间逻辑、覆盖(supersession)和遵循指令。我们将来可能会暴露数据用于编程查询,但我们预计那对边缘情况有用,而非常见情况。

我们构建 Agent Memory,是因为我们在平台上看到的工作负载暴露了现有方法没有完全解决的差距。运行数周或数月、对接真实代码库和生产系统的 agent,需要随着内存增长仍然有用——而不仅仅是在一个干净的、可能完全装得下新模型上下文的 benchmark 数据集上表现好。

它们需要快速摄取。它们需要不会阻塞对话的检索。它们需要在保持每次查询成本合理的模型上运行。

你怎么使用它

Agent Memory 把记忆存储在一个 profile 中,profile 通过名字寻址。一个 profile 给你提供几种操作:摄取一段对话、记住某些特定的东西、回忆你需要的内容、列出记忆,或者忘掉某条特定的记忆。Ingest 是 bulk 路径,通常在 harness 压缩上下文时被调用。Remember 是模型用来当场存储重要内容的方式。Recall 运行完整的检索管线并返回一个综合答案。

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // Get a profile -- an isolated memory store shared across sessions, agents, and users
    const profile = await env.MEMORY.getProfile("my-project");
    // Ingest -- extract memories from a conversation (typically called at compaction)
    await profile.ingest([
      { role: "user", content: "Set up the project with React and TypeScript." },
      { role: "assistant", content: "Done. Scaffolded a React + TS project targeting Workers." },
      { role: "user", content: "Use pnpm, not npm. And dark mode by default." },
      { role: "assistant", content: "Got it -- pnpm and dark mode as default." },
    ], { sessionId: "session-001" });
    // Remember -- store a single memory explicitly (direct tool use by the model)
    const memory = await profile.remember({
      content: "API rate limit was increased to 10,000 req/s per zone after the April 10 incident.",
      sessionId: "session-001",
    });
    // Recall -- retrieve memories and get a synthesized answer
    const results = await profile.recall("What package manager does the user prefer?");
    console.log(results.result); // "The user prefers pnpm over npm."
    return Response.json({ ok: true });
  },
};

Agent Memory 通过 binding 从任何 Cloudflare Worker 访问。它也可以通过 REST API 给运行在 Workers 之外的 agent 访问,遵循与其他 Cloudflare 开发者平台 API 相同的模式。如果你正在使用 Cloudflare Agents SDK,Agent Memory 服务可以整齐地集成,作为 Sessions API 的内存部分中处理压缩、记忆和搜索的参考实现。

你能用它构建什么

Agent Memory 被设计为可与一系列 agent 架构一起工作:

为单个 agent 提供内存。 无论你正在构建带人工参与的 Claude Code 或 OpenCode 这样的编码 agent,使用 OpenClaw 或 Hermes 这样的自托管 agent 框架代你行事,还是接入 Anthropic 的 Managed Agents 这样的托管服务,Agent Memory 都可以作为持久内存层而无需对 agent 的核心循环做任何更改。

为自定义 agent harness 提供内存。 许多团队正在构建自己的 agent 基础设施,包括无人参与下自主运行的后台 agent。Ramp Inspect 是一个公开的例子;StripeSpotify 也描述过类似的系统。这些 harness 也可以从给它们的 agent 提供跨会话持久且能在重启后存活的记忆中受益。

跨 agent、人和工具的共享内存。 一个内存 profile 不必属于单个 agent。一个工程师团队可以共享一个内存 profile,这样某个人的编码 agent 学到的知识对所有人都可用:编码约定、架构决策、目前还活在人们脑子里、或者在上下文裁剪时丢失的部落知识。一个 code review bot 和一个编码 agent 可以共享内存,这样 review 反馈可以塑造未来的代码生成。你的 agent 积累的知识不再是短暂的,而开始成为持久的团队资产。

虽然搜索是内存的一个组成部分,但 agent search 和 agent memory 解决不同的问题。AI Search 是我们用于在非结构化和结构化文件上查找结果的原语;Agent Memory 用于上下文回忆。Agent Memory 中的数据不以文件形式存在;它派生自会话。一个 agent 可以同时使用两者,它们被设计为协同工作。

你的记忆属于你

随着 agent 变得更强大、更深入地嵌入到业务流程中,它们积累的内存变得真正有价值——不仅作为运营状态,而是作为通过实际工作建立起来的机构知识。我们听到客户对将这种资产绑定到单一供应商日益增长的担忧,这是合理的。Agent 学到的越多,如果该内存不能随它一起迁移,切换成本就越高。

Agent Memory 是一项托管服务,但你的数据属于你。每条记忆都可以导出,我们承诺确保你的 agent 在 Cloudflare 上积累的知识在你的需求改变时可以与你一起离开。我们认为赢得长期信任的正确方式是让离开变得容易,并继续构建好到你不想离开的东西。

Agent Memory 的工作方式

要理解上面 API 背后发生了什么,有必要分解一下 agent 是如何管理上下文的。一个 agent 有三个组件:

  1. 一个 harness,驱动对模型的重复调用,促成工具调用,管理状态。

  2. 一个 model,接受上下文并返回 completion。

  3. State,既包括当前的上下文窗口,也包括上下文之外的额外信息:对话历史、文件、数据库、内存。

一个 agent 上下文生命周期中的关键时刻是 compaction,当 harness 决定缩短上下文以便保持在模型的限制内或避免 context rot 的时候。今天,大多数 agent 永久丢弃信息。Agent Memory 在 compaction 时保留知识而不是丢失它。

Agent Memory 以两种方式集成到这个生命周期中:

  1. Compaction 时的批量摄取。 当 harness 压缩上下文时,它将对话发给 Agent Memory 进行摄取。摄取从消息历史中提取事实、事件、指令和任务,与已有内存去重,并将它们存储为未来检索的内存。

  2. 模型的直接工具使用。 模型获得直接与内存交互的工具,包括 recall(在内存中搜索特定信息)的能力。模型还可以 remember(根据某个重要的东西显式存储记忆)、forget(标记某条记忆不再重要或为真)以及 list(查看存储了哪些记忆)。这些是不需要模型设计查询或管理存储的轻量操作。主 agent 永远不应在存储策略上烧上下文。它看到的工具表面被故意约束,以使内存不挡在实际任务的路上。

摄取管线

当一段对话到达进行摄取时,它会经过一个多阶段管线,提取、验证、分类并存储记忆。

第一步是确定性 ID 生成。每条消息得到一个内容寻址的 ID——一个对 session ID、role 和 content 进行 SHA-256 哈希,截断为 128 位的值。如果同一段对话被摄取两次,每条消息都会解析到相同的 ID,使得重新摄取是幂等的。

接下来,提取器并行运行两个 pass。一个 full pass 在大约 10K 字符处对消息分块,有两条消息的重叠,并发处理最多四个 chunk。每个 chunk 得到一个带 role label、相对日期解析为绝对值(“yesterday” 变成 “2026-04-14”)以及行索引(用于来源溯源)的结构化转录。对于较长的对话(9+ 条消息),一个 detail pass 与 full pass 并行运行,使用重叠的窗口,专门关注提取像名字、价格、版本号和实体属性这样的具体值,这些是粗粒度提取容易遗漏的。然后两个结果集被合并。

下一步是对照源转录验证每条提取的记忆。验证器运行八项检查,涵盖实体身份、对象身份、位置上下文、时间准确性、组织上下文、完整性、关系上下文,以及推断的事实是否实际由对话支持。每项被相应地通过、修正或丢弃。

然后管线把每条经过验证的记忆分类到四种类型之一。

  • Facts(事实)代表当下为真的内容,即原子的、稳定的知识,比如“项目使用 GraphQL“或“用户偏好 dark mode“。

  • Events(事件)捕获在某个特定时刻发生的事情,比如一次部署或一个决定。

  • Instructions(指令)描述如何做某件事,例如流程、工作流、runbook。

  • Tasks(任务)追踪当前正在进行的工作,设计上是短暂的。

Facts 和 instructions 是带 key 的。每个都得到一个规范化的主题 key,当一条新记忆与已有的有相同的 key 时,旧记忆会被覆盖而不是删除。这创建了一个版本链,有从旧记忆指向新记忆的前向指针。Tasks 完全被排除在向量索引之外以保持精简,但仍然可以通过全文搜索发现。

最后,所有内容都使用 INSERT OR IGNORE 写入存储,这样内容寻址的重复项会被静默跳过。在向 harness 返回响应之后,后台向量化异步运行。嵌入文本将分类期间生成的 3-5 条搜索查询前置到记忆内容之前,弥合了记忆的写入方式(声明式:“用户偏好 dark mode”)和搜索方式(疑问式:“用户想要什么主题?”)之间的差距。已被覆盖的记忆的向量与新的 upsert 并行删除。

检索管线

当 agent 搜索一条记忆时,查询会经过一个独立的检索管线。在开发期间,我们发现没有单一的检索方法对所有查询都是最好的,所以我们并行运行多种方法并融合结果。

第一阶段并发地运行查询分析和嵌入。查询分析器产生排序后的主题 key、带同义词的全文搜索词,以及一个 HyDE(Hypothetical Document Embedding),即一段以陈述句形式表述、就好像它是问题答案的话。该阶段直接嵌入原始查询,两个嵌入都用于下游。

在下一阶段,五个检索通道并行运行。带 Porter stemming 的全文搜索处理你知道精确术语但不知道周围上下文的查询的关键词精度。精确 fact-key 查找返回查询直接映射到已知主题 key 的结果。Raw message search 通过全文搜索直接查询存储的对话消息,作为对未分类对话片段的安全网,捕获提取管线可能已经泛化掉的逐字细节。Direct vector search 使用嵌入的查询找到语义上相似的记忆。HyDE vector search 找到与答案外观相似的记忆,这通常浮出 direct embedding 错过的结果——尤其是对于问题和答案使用不同词汇的抽象或多跳查询。

在第三个也是最后一个阶段,所有五个检索通道的结果使用 Reciprocal Rank Fusion(RRF)合并,每个结果根据其在给定通道中的排名获得加权分数。Fact-key 匹配获得最高权重,因为精确的主题匹配是最强的信号。全文搜索、HyDE 向量和直接向量分别根据信号强度加权。最后,raw message 匹配也以低权重包含,作为安全网,识别提取管线可能错过的候选结果。打平由 recency 决定,较新的结果排名更高。

然后管线将顶部候选传给 synthesis 模型,后者生成对原始搜索查询的自然语言回答。某些特定查询类型得到特殊处理。例如,时间计算通过 regex 和算术确定性地处理,而不是由 LLM 处理。结果作为预先计算的事实注入合成提示中。模型在日期数学这类事情上不可靠,所以我们不让它们做。

我们如何构建它

我们对 Agent Memory 的初始原型很轻量,有一个基本的提取管线、向量存储和简单的检索。它工作得足以演示概念,但不足以发布。

所以我们把它放进一个 agent 驱动的循环并迭代。循环看起来是这样的:运行 benchmark,分析我们有差距的地方,提出方案,让一个人审阅这些方案以选择能够泛化而不是过拟合的策略,让 agent 进行更改,重复。

这工作得不错,但带来一个具体的挑战。LLM 是随机的,即使温度设为零。这导致结果在多次运行之间变化,这意味着我们必须对多次运行做平均(对大型 benchmark 很耗时),并依靠趋势分析与原始分数一起来理解什么真正起作用。一路上我们必须仔细防止以那些并未真正使产品对一般情况更好的方式过拟合 benchmark。

随着时间推移,这让我们达到了一个状态:benchmark 分数在每次迭代中都一致地改善,我们有了一个能在真实世界中工作的通用化架构。我们故意针对多个 benchmark(包括 LoCoMo、LongMemEval 和 BEAM)进行测试,以从不同方面推动系统。

为什么是 Cloudflare

我们在 Cloudflare 上构建 Cloudflare,Agent Memory 也不例外。强大且易于组合的现有原语让我们能在一个周末发布第一个原型,在不到一个月的时间内发布一个功能完整、生产化的 Agent Memory 内部版本。除了交付速度之外,Cloudflare 还由于其他几个原因被证明是构建这种服务的理想之地。

在底层,Agent Memory 是一个协调几个系统的 Cloudflare Worker:

  • Durable Object:存储原始消息和已分类的记忆

  • Vectorize:在嵌入的记忆上提供向量搜索

  • Workers AI:运行 LLM 和嵌入模型

每个内存上下文映射到自己的 Durable Object 实例和 Vectorize 索引,在上下文之间保持数据完全隔离。它也让我们随着更高需求轻松扩展。

通过 Durable Objects 进行计算隔离。 每个内存 profile 得到自己的 Durable Object(DO)以及一个 SQLite-backed 的存储,在租户之间提供强隔离而无需任何基础设施开销。DO 处理 FTS 索引、覆盖链和事务性写入。DO 的 getByName() 寻址意味着任何来自任何地方的请求都可以通过名字到达正确的内存 profile,并确保敏感记忆与其他租户强隔离。

横跨整个栈的存储。 内存内容存放在 SQLite-backed 的 DO 中。向量存放在 Vectorize 中。未来,快照和导出将进入 R2,以便进行成本高效的长期存储。每个原语都是为其工作负载量身打造的,我们不需要把所有东西强行塞进单一的形状或数据库。

用 Workers AI 进行本地模型推理。 整个提取、分类和合成管线运行在部署在 Cloudflare 网络上的 Workers AI 模型上。所有 AI 调用传递一个 session affinity header,路由到内存 profile 名,这样重复请求会命中同一个后端,以获得 prompt caching 的好处。

我们模型选择中一个有趣的发现:更大、更强的模型并不总是更好。我们目前默认使用 Llama 4 Scout(17B,16-expert MoE)用于提取、验证、分类和查询分析,以及 Nemotron 3(120B MoE,12B 活动参数)用于合成。Scout 高效地处理结构化分类任务,而 Nemotron 更大的推理能力提升了自然语言回答的质量。Synthesizer 是把更多参数投入问题始终能改善结果的唯一阶段。对于其他一切,较小的模型在成本、质量和延迟之间命中了更好的甜蜜点。

我们如何使用它

我们在 Cloudflare 内部为我们自己的工作流运行 Agent Memory,既作为实验场,也作为接下来要构建什么的灵感来源。

编码 agent 内存。 我们使用一个内部的 OpenCode 插件,把 Agent Memory 接入开发循环。Agent Memory 提供会话内以及跨会话的过往压缩内存。一个不那么明显的好处是跨团队的共享内存:有了共享 profile,agent 知道你的团队其他成员已经学到了什么,这意味着它可以停止问已经被回答的问题,停止犯已被纠正的错误。

Agentic 代码评审。 我们已经把 Agent Memory 连接到我们内部的 agentic 代码 reviewer。可以说它学到的最有用的事情是保持安静。Reviewer 现在记得某条特定的评论在过去的 review 中并不相关、某种特定的模式被标记过、而作者出于充分理由选择保留它。Review 随时间变得更不嘈杂,而不只是更聪明。

聊天机器人。 我们也把内存接入了一个内部聊天机器人,它摄取消息历史然后潜伏并记住新发送的消息。然后,当有人问问题时,机器人可以基于以前的对话来回答。

我们还有一些额外的用例,计划在我们完善和改进服务之时近期在内部推出。

接下来是什么

我们持续在内部测试和完善 Agent Memory,改进提取管线、调优检索质量、扩展后台处理能力。类似于人类大脑通过在睡眠中重放和强化连接来巩固记忆,我们看到内存存储异步改进的机会,目前正在实施和测试各种策略以使其工作。

我们计划很快公开发布 Agent Memory。如果你正在 Cloudflare 上构建 agent 并希望提前访问,请联系我们加入 waitlist

如果你想深入了解架构、分享你正在构建什么,或在我们继续开发的过程中跟随,请加入我们的 Cloudflare Discord 或在 Cloudflare Community 开个 thread。我们正在积极关注两者,对生产 agent 工作负载在野外实际是什么样子很感兴趣。