构建真正会记忆的提醒智能体:Mem0 与 Claude Agent SDK 实践

原文:Building Memory-First AI Reminder Agents with Mem0 and Claude Agent SDK 作者:Mem0 Team

传统的提醒机器人只会接收命令、安排任务、发送通知,然后忘掉整个交互过程。它们不会学习你是偏好早上还是晚上的通知,不会记录你通常延后 5 分钟还是 30 分钟,一旦你把某事标记为完成,上下文就从系统里彻底消失了。

这对闹钟来说没问题。但如果你需要一个能在数周甚至数月间记住你的行为模式、同时不丢掉真正需要处理的事务的助手,这种方式就行不通了。

一个真正好用的提醒助手需要在可靠性和个性化之间取得平衡。它绝不能忘记任何一个活跃的提醒,绝不能重复触发已完成的提醒,同时还要学习诸如”这个用户平均将工作任务延后 15 分钟”或”个人提醒集中在晚上 8 点左右”这类行为模式。

Mem0,我们构建了 RecallAgent——一个 Slack 提醒机器人——来探索当记忆(Memory)作为核心架构组件而非附加功能时会发生什么。其结果是一套构建 AI 智能体的范式:会记忆但不会产生幻觉,会适应但不会漂移,会归档但不会丢失历史记录

本文从两个部分来剖析这个系统:首先是产品体验以及它为什么和常规机器人表现不同,然后是足够的工程细节,让你能构建类似的东西。

这个智能体使用 Claude Agent SDK 作为决策引擎,通过 Slack 事件进行交互,用关系型数据库(本地 SQLite,生产环境 Supabase)存储状态。长期个性化能力来自 Mem0 记忆模型,FastAPI 后端部署在 Render 免费层等托管服务上。

第一部分:产品故事

RecallAgent 的行为就像一个现代的 Slack 助手。你可以自然地跟它对话:

“提醒我明天交房租。”

“把那个推迟到下午 6 点。”

“延后 10 分钟。”

“这周有什么事?”

当提醒触发时,它会通过 Slack 直接 ping 你,附上上下文、按钮和内联响应选项。你可以标记完成、延后提醒、或询问详情,这一切都不会打断对话流程。

Slack 提醒通知

差异会在使用中逐渐显现。如果你经常在上午 10 点左右确认工作提醒,RecallAgent 就会开始在你创建不指定时间的提醒时建议这个时间点。如果你持续选择延后约 15 分钟,智能体会学习这个间隔并反映出来。当你完成一个提醒后,它不再影响未来的行为,但依然可以在历史记录中搜索到。

大多数 AI 系统要么遗忘得太激进(丢失有价值的行为模式),要么记住得太松散(造成状态矛盾)。RecallAgent 通过**区分”什么是事实”和”什么是记忆”**来避免这两个问题。

真相 vs 记忆

在 RecallAgent 中,提醒不是记忆,是事实。如果一条提醒存在,它就必须触发;如果它被标记为完成,就永远不能再触发。这个不变量存在于数据库中,并在每一轮对话中被注入到系统提示词中。模型看到的是实际的提醒状态,而不是对它的”回忆”。

记忆存储的则是完全不同的东西:行为、偏好和上下文。记忆回答的是:“这个用户如何表述提醒""他们通常在什么时间点确认""他们延后的频率""他们隐式使用了哪些分类”。Mem0 存储这些信号,并维护一份活跃和已完成提醒的轻量记录,这样助手就可以回忆历史,而不会把它当作事实。我们把活跃和已归档的提醒写入 Mem0 分类中,但模型永远不会把这些视为权威来源。数据库是真相,Mem0 是个性化层

这条边界让系统既具有适应性又保持正确。智能体可以在不成为真相来源的前提下学习。

系统组件

RecallAgent 有四个主要部分:Slack 作为交互界面,FastAPI 后端负责编排一切,关系型数据库存储提醒状态,Mem0 负责长期记忆。一个基于 LLM 的智能体位于中心,但它从不直接修改状态。它负责推理、决策并调用工具。所有状态变更都在确定性代码中执行,模型由 Claude Agent SDK 驱动,配合受约束的工具接口。

架构图

高层架构:Slack → FastAPI → 智能体,数据库和 Mem0 作为底座。

这种结构是刻意保守的。当你在构建处理时间、通知和信任的智能体时,“无聊的架构”本身就是一种特性。

第二部分:工程细节

本节提供实现蓝图。我们从 Slack 发送事件的位置开始,逐步深入到编排、记忆和状态管理。

Slack 集成

Slack 是入口点,消息和按钮点击都从这里到达你的后端。Slack 的事件投递是至少一次语义,因此重复消息是预期行为,而非边缘情况。

后端暴露三个 HTTP 端点:POST /slack/events 用于事件 API 消息,POST /slack/commands 用于斜杠命令,POST /slack/interactions 用于按钮点击。每个入站请求都使用 Slack 的签名密钥进行认证以防止重放攻击,参照 Slack 验证指南。在任何业务逻辑运行之前,事件会使用一个以 Slack 事件 ID 为键的短 TTL 缓存进行去重。如果没有去重,生产环境中你会”加倍”创建提醒。

Slack 特有的格式(@提及、频道标签、标记语法)会被立即剥离。智能体只看到干净文本。这简化了意图处理并减少了提示词噪音。

智能体循环

一旦消息被规范化,它就会进入一个单一的编排循环,优先在模型推理之前处理确定性的状态转换。我们首先解析内存中存储的任何待处理状态转换:缺失时间的跟进、来自记忆层的个性化时间建议、或多个匹配提醒之间的澄清。像”是的""不""第二个”这样的简短回复,通过简单规则而非再一次模型调用来映射到显式状态更新。只有在状态明确无误后,我们才调用 LLM 选择工具并产出结构化操作。这个顺序是精心设计的——当生成式输出可以覆盖不完整或模糊的状态时,提醒流程就会出问题。

记忆检索

记忆查询是有意图的,而非自动的。系统首先检查用户的意图,只有当个性化能带来帮助时才检索 Mem0 信号。列出提醒几乎不需要记忆;创建或澄清提醒则常常需要。当检索有必要时,我们只查询 Mem0 的两个分类(偏好和行为摘要),然后用短 TTL 缓存结果。这保持了提示词的精简、延迟的可预测,并让记忆噪音远离模型的决策路径。

记忆检索流程

记忆检索流程:意图检查、Mem0 查询和短 TTL 缓存。

结果是一个既个性化又不臃肿或矛盾的提示词。

Claude Agent 集成

我们集成 Claude Agent SDK 作为编排层,但将其接触面保持得足够窄。智能体不直接修改状态。它负责推理、选择工具并返回结构化输入。确定性的、由 SQL 支撑的代码执行变更、记录日志并同步记忆。这种分离确保系统在模型不可避免地出错时依然可信赖。

工具集故意保持小而强类型化。每个工具映射到 agent-backend/main.py 中的一个具体函数,数据库是真相来源,Mem0 提供长期上下文。没有这个工具边界,你就会丢失可审计性,并打开幻觉更新的大门。

工具功能
create_reminder使用自然语言时间解析和分类推断创建提醒
update_reminder修改标题、描述或到期时间;标记重新调度以进行行为追踪
mark_done完成提醒并将其记忆移至归档分类
snooze_reminder推迟提醒并记录延后间隔
list_reminders返回数据库支撑的列表(活跃、已完成、全部),格式稳定
search_reminders在数据库中按标题/描述搜索提醒
delete_reminder仅在用户明确要求时删除提醒
set_preference将长期偏好写入 Mem0 记忆
get_preferences从 Mem0 读取偏好并返回给智能体
list_rescheduled_reminders从 Mem0 或数据库展示有重新调度历史的提醒
clarify_reminder当多个提醒匹配时让用户澄清

系统提示词

系统提示词不是创意写作,而是契约。它明确告诉模型:数据库是提醒的真相来源,Mem0 仅提供个性化,模型绝不能编造或假设提醒状态,所有状态变更必须通过工具完成。Claude 获得一组狭窄的工具:创建、更新、延后、列出、标记完成。当它认为需要一个操作时,输出结构化工具调用。提示词内部不发生任何状态变更。

这种设计让系统可审计、可测试,并能抵抗模型漂移。

提示词构建

提示词从两个具有不同保证级别的来源组装。首先,从数据库加载当前提醒状态并直接渲染进系统提示词。这让模型对现实的视图是确定性的——它不能产生幻觉或覆盖实际的提醒状态。其次,用 Mem0 信号(偏好和行为摘要)来丰富,让智能体可以适应但不会漂移。我们刻意保持这一记忆片段的精简和针对性。结果是一个既感觉个性化又保持脚踏实地的提示词。它建议默认时间或解读用户模式,而不会模糊记忆和状态之间的边界。

提醒创建与缺失时间

用户经常省略时间:“明天""稍后""晚上”。猜测很危险,反复询问很烦人。RecallAgent 通过推理加确认来解决这个问题。如果没有提供时间,系统推断一个分类,并从行为记忆中查找用户在该分类下最常见的确认时间。它提出一个默认值,并在调度前请求明确确认。这在保持用户信任的同时减少了摩擦。

数据库

所有提醒存储在关系型数据库中(本地 SQLite,生产环境 Supabase)。表结构追踪提醒、状态、审计日志、行为统计和短期对话窗口。

数据库表结构

数据库表结构概览:提醒、偏好、审计日志和行为统计。

数据库让系统变得可靠。如果 Mem0 不可用,提醒依然会触发。如果模型行为异常,状态依然正确。开发过程中浮现出一条有用的规则:如果丢失数据会破坏正确性,它就属于数据库;如果丢失数据只会减少个性化,它就属于记忆

Mem0 实现

Mem0 使用明确的分类来存储长期信号和镜像的提醒历史:活跃提醒、已归档提醒、用户偏好、行为摘要和可选的对话记忆。当提醒被标记为完成后,其活跃记忆被移除,代之以归档记忆。这避免了陈旧记忆影响未来行为,同时让历史记录可搜索。镜像记忆从不被视为权威状态——它改善回忆和个性化,但不决定什么该触发。行为被摘要化而非原始记录。模型看到的是模式而非噪音。RecallAgent 在持续学习的同时不会积累矛盾。

通知与归档

提醒投递作为独立于对话智能体的执行路径运行。后台轮询循环查询在提前时间窗口内到期的提醒,并通过 Slack 推送带有操作按钮的通知。每条提醒记录 last_notified_at,这样我们可以保证幂等投递,避免在重试或重启时重复 ping。

归档通过一个计划的 cron 任务以带外方式处理,该任务调用端点将 SQL 存储中的过期提醒从活跃状态更新为已完成。这使基于时间的状态转换与聊天流量解耦,确保即使 web 进程休眠,系统仍保持正确。

Slack 应用设置

后端端点只是故事的一半。要让机器人真正运行起来,你需要在 Slack 应用仪表板(api.slack.com/apps)注册一个 Slack 应用并指向你的公开 URL。创建应用后,启用 Events、Commands 和 Interactivity,并填入你的 FastAPI 服务暴露的端点。对于 RecallAgent,应用期望 /slack/events 处理事件回调,/slack/commands 处理斜杠命令,/slack/interactions 处理按钮操作。签名密钥和 bot token 存储在后台环境变量中,以便服务验证请求并将响应发回 Slack。

Slack 要求公开的 HTTPS 端点。本地开发时,用 ngrok 等隧道工具暴露服务器,在 Slack 应用设置中使用其 URL。生产环境部署到托管服务(Render、Fly、Railway 等),使用稳定的 URL 处理事件和交互回调。免费层托管通常会在不活动后引入冷启动,因此新对话中的第一次请求延迟会稍高。

Slack 应用设置

Slack 应用设置中的 Events 和 Interactivity URL。

为什么这个架构可行

这个架构的价值不在于它能发送提醒,而在于它能规模化信任。它让你可以在不让记忆成为真相的前提下实现个性化,让你可以在不抹除历史的前提下进行归档。智能体可以从行为中学习,而系统保持确定性、可审计和安全。

这种平衡正是 Mem0 这类记忆层发挥作用的地方。Claude 平台赋予模型推理能力,但可靠性来自于我们在状态和工具之间强制执行的契约。如果你在构建一个要活过 demo 阶段的智能体,状态和记忆之间的这条边界不是可选项——它是一次性 demo 和用户每天信赖的系统之间的分水岭。

记忆只有在刻意使用时才创造价值。我们选择 Mem0 来承载偏好和行为等持久信号,而 SQL 存储始终是时间和状态的唯一真相来源。这种分离让智能体感觉人性化而又不会变得不可预测。如果你基于这个范式构建,你的助手就不仅仅是响应——它会在合适的时机、以合适的理由记住正确的事情,并赢得让长生命周期智能体成为可能所必需的信任。

查看 Mem0 文档 了解更多集成记忆功能的信息。也可以通过邮件联系我们。


译者注:本文核心洞察是”真相 vs 记忆”的架构分离——数据库管正确性(事实),Mem0 管个性化(偏好/行为)。这种两层设计是构建可信任长生命周期 AI Agent 的关键范式,值得所有 Agent 开发者参考。文中提到的工具集设计——“模型只推理选工具、确定性代码执行变更”——也是工程实践上的最佳范例。