二、什么是 Agent 记忆服务
大语言模型本身是无状态的:会话一结束,模型就会“忘记”用户的偏好、历史决策和上下文信息。Agent 记忆服务解决的核心问题,就是让 AI 应用具备跨会话的长期记忆。
服务接收对话内容后,由腾讯云大模型服务平台托管的模型自动完成事实提取、摘要结构化与向量化,将值得记住的信息持久化到你的云数据库 PostgreSQL 实例中(基于 pgvector 向量索引);在后续交互时,再通过语义检索把相关记忆召回,供应用注入到 Prompt 中,实现“越用越懂用户”的体验。
与自行搭建“向量库 + 缓存 + 图库”的多组件方案相比,Agent 记忆服务以云数据库 PostgreSQL 实例为统一存储底座,模型调用与向量索引全部托管,你只需面向一组简洁的 REST API 编程,无需运维额外中间件。
注意:说明:
注意:Agent 记忆服务是基于开源项目 mem0 构建的托管服务,使用方式完全兼容社区用法。熟悉 mem0 的开发者可以零成本迁移:社区的调用示例、SDK 封装与集成模式均可直接复用;同时免去自行部署和运维 LLM、Embedding、向量库的成本,模型与存储均由腾讯云托管。
三、核心概念
| 概念 | 说明 |
| 记忆(Memory) | 从对话中提取出的结构化事实,例如用户偏好、身份信息、历史决策等。每条记忆拥有唯一 ID 与内容哈希(hash),并记录创建与更新时间 |
| user_id | 用户维度标识。用于区分不同终端用户的记忆,是最常用的隔离维度 |
| agent_id | Agent 维度标识。同一用户在不同 Agent(如客服 Agent、代码 Agent)下的记忆相互隔离 |
| run_id | 运行实例维度标识。用于区分不同会话批次或任务执行过程产生的记忆 |
| 记忆事件(event) | 写入操作的处理结果,取值:ADD(新增)、UPDATE(更新已有记忆)、DELETE(删除冲突记忆)、NOOP(无需变更) |
注意:说明:
注意:三个隔离维度可按业务需要组合使用,但每次写入至少需提供其中一个。不同维度下的记忆互不干扰,检索时也仅在指定维度范围内进行。
四、工作原理
Agent 记忆服务的工作过程分为写入链路与召回链路两部分:
1. 提交对话:应用将一轮对话的 messages 数组提交至记忆服务。
2. 事实提取:托管大模型自动识别值得记住的事实,并比对已有记忆决定 ADD / UPDATE / DELETE。
3. 向量化存储:记忆文本生成向量后持久化至云数据库 PostgreSQL(pgvector 索引)。
4. 语义检索:应用以自然语言发起检索,返回相关度最高的记忆及相似度得分。
5. 上下文注入:应用将召回的记忆拼入 Prompt,供大模型生成个性化回复。
6. 记忆回写:本轮对话再次提交至记忆服务,形成记忆闭环。
五、适用场景
| 场景 | 记忆服务解决什么 | 建议的维度设计 |
| AI 编程助手 | 跨会话记住项目技术栈、代码规范、目录约定,无需每次重新交代 | user_id + agent_id |
| 智能客服 | 记住客户历史工单、产品版本、沟通偏好,避免重复询问 | user_id |
| 运维助手 | 记住实例清单、变更窗口偏好、历史故障根因 | user_id + agent_id |
| 个人助理 | 记住长期目标、日程习惯、个人偏好 | user_id |
| 多 Agent 协作 | 各角色积累各自领域经验,共享用户画像 | 共享 user_id,独立 agent_id |
| 长流程任务 Agent | 多步骤任务中保持中间结论,任务结束批量回收 | user_id + run_id |
七、开通记忆服务
记忆服务依赖云数据库 PostgreSQL 实例与 AgenticBase 套餐(提供 tencentdb_ai AI 扩展插件),请确认两者已就绪后,通过以下任一方式开通记忆服务:
| 开通方式 | 操作入口 | 适用场景 |
| 控制台开通 | AgenticBase 详情页的「mem0 记忆服务」区域,选择 LLM 模型、填入 Embedding API Key 后单击「开启应用」 | 手动操作、首次开通 |
| 云 API 开通 | 调用 OpenMem0Service 接口 | 自动化、CI/CD 与批量开通 |
| Skill 一句话部署 | 在 CodeBuddy / WorkBuddy 等 AI 客户端中安装 PostgreSQL Skill,用自然语言下达开通指令 | 对话式操作,一轮对话完成部署 |
通过 PostgreSQL Skill 一句话部署
TencentDB PostgreSQL Skill 是面向云数据库 PostgreSQL 的任务型技能包,其中的 mem0 部署技能(tencent-pg-mem0-deploy)可将开通流程简化为一句自然语言指令:
示例输入:
帮我给广州 postgres-abc12345 开通 mem0,AgenticBaseId 用 agenticbase-xxxxx,Embedding Key 从环境变量 TENCENT_TOKENHUB_API_KEY 读取
其中 Embedding API Key 为开通的必填参数(sk- 开头),需提前在 腾讯云大模型服务平台 TokenHub 的 Embedding 模型详情页创建,并通过环境变量 TENCENT_TOKENHUB_API_KEY 或密钥管理服务注入。请勿在聊天、URL、日志或代码仓库中粘贴真实 Key。技能收到指令后会自动完成以下动作,无需你逐步操作:
1. 解析必要信息:自动提取地域、实例 ID、AgenticBaseId、模型等非敏感参数;若未检测到 Embedding API Key,会提示你配置运行时环境变量,不会要求在聊天中提供凭证。
2. 预检查:先查询实例适配性和 mem0 当前状态,避免重复开通。
3. 执行并轮询:目标明确且无阻塞时直接执行开通动作,持续轮询直到服务就绪。
4. 返回结果:输出实例信息、执行的操作、TaskId、耗时与最终结果。
除开通外,该技能同样支持「关闭 mem0 服务」「获取可用地址」等指令。Skill 包可通过 GitHub、ClawHub 或 SkillHub 获取,安装到 CodeBuddy / WorkBuddy 等客户端后即可使用。
注意:说明:
注意:Skill 遵循「先查后写」原则:所有场景都会先查询当前状态再判断是否执行写动作,高风险动作需你确认后才会执行;Embedding API Key 等凭证仅从运行时环境读取,不会写入聊天记录、URL、日志或仓库文件。
通过云 API 开通
记忆服务的开通、关闭与详情查询通过以下三个云 API 完成。相关接口动作已纳入 腾讯云 CAM 权限文档;当前产品文档尚未提供完整的接口参数详情页,具体参数与可用性请以 API Explorer 实际展示为准:
| 接口名 | 功能 | 频率限制 |
| OpenMem0Service | 开启实例的记忆服务 | 20 次/秒 |
| CloseMem0Service | 关闭实例的记忆服务 | 20 次/秒 |
| DescribeMem0Service | 查询记忆服务详情(含服务地址) | 20 次/秒 |
公共请求参数(三个接口通用):
| 参数 | 取值 |
| 请求域名 | postgres.tencentcloudapi.com |
| API 版本 | 2017-03-12 |
| 签名方法 | TC3-HMAC-SHA256(v3 签名) |
| 请求方法 | POST |
| Content-Type | application/json |
注意:说明:
注意:推荐使用 API Explorer(OpenMem0Service) 在线调试,可自动生成 TC3 签名与多语言 SDK 代码,避免手写签名逻辑出错。
OpenMem0ServiceCloseMem0ServiceDescribeMem0Service
开启指定实例的记忆服务。
输入参数
| 参数 | 必填 | 类型 | 说明 |
| DBInstanceId | 是 | String | PostgreSQL 实例 ID,如 postgres-paxanz0n |
| AgenticBaseId | 是 | String | AgenticBase 套餐 ID,如 agenticbase-2a7vajd1(需先开通) |
| LLMModel | 是 | String | 用于事实提取的 LLM,可选值见下方模型列表 |
| EmbeddingApiKey | 是 | String | TokenHub Embedding 服务的 API Key(sk- 开头),获取方式见 上文 Skill 部分说明 |
LLMModel 可选值
| 系列 | 可选值 |
| 自动路由 | auto(推荐首选,由平台自动路由到合适的模型) |
| DeepSeek | deepseek-v4-flash、deepseek-v4-pro |
| GLM | glm-5、glm-5-turbo、glm-5.1 |
| Kimi | kimi-k2.5、kimi-k2.6 |
| MiniMax | minimax-m2.5、minimax-m2.7 |
选择建议:不确定时选 auto;抽取质量优先选 -pro / 高版本型号;抽取延迟与成本优先选 -flash / -turbo 型号。变更模型会导致新旧记忆的提取风格不一致,建议在正式使用前确定,避免上线后频繁切换。
输出参数
TaskId(任务 ID)、RequestId(请求 ID)。开通为异步任务,可通过 DescribeMem0Service 轮询状态。
请求示例
POST / HTTP/1.1Host: postgres.tencentcloudapi.comContent-Type: application/jsonX-TC-Action: OpenMem0Service<公共请求参数>{ "DBInstanceId": "postgres-paxanz0n", "AgenticBaseId": "agenticbase-2a7vajd1", "LLMModel": "auto", "EmbeddingApiKey": "sk-xxxxxxxx"}
关闭指定实例的记忆服务。
输入参数
| 参数 | 必填 | 类型 | 说明 |
| DBInstanceId | 是 | String | PostgreSQL 实例 ID |
输出参数
TaskId(任务 ID)、RequestId(请求 ID)。
请求示例
POST / HTTP/1.1Host: postgres.tencentcloudapi.comContent-Type: application/jsonX-TC-Action: CloseMem0Service<公共请求参数>{ "DBInstanceId": "postgres-pax**z0n"}
注意:关闭后服务状态变为 none,记忆接口不再可用。关闭前请确认已完成数据处置:记忆数据如仍有价值,建议先通过 GET /memories 按维度导出,或对实例做一次备份。
查询指定实例的记忆服务详情,服务访问地址(InnerAddress)通过该接口获取。输入仅需 DBInstanceId。
返回字段
| 字段 | 类型 | 说明 |
| Status | String | 运行状态:creating(开通中)/ running(运行中)/ deleting(关闭中)/ none(未开通) |
| InnerAddress | String | 记忆服务访问地址,如172.16.32.14:8000,即这篇中的 <服务地址> |
| CreateTime / UpdateTime | String | 服务创建时间 / 最后更新时间 |
| AgenticBaseId | String | 关联的 AgenticBase 套餐 ID |
| LLMMode | String | LLM 提供方,取值 tokenhub |
| LLMModel | String | 当前使用的 LLM 模型 |
| EmbeddingModel | String | 当前使用的 Embedding 模型 |
| EmbeddingDims | Integer | 向量维度,固定1024 |
| PGDatabaseName | String | 记忆数据所在数据库,固定 mem0 |
| PGUserName | String | 记忆服务使用的数据库账号,固定 mem0_user |
| NetworkAccessList | Array | 网络信息(DBInstanceNetInfo 数组) |
请求示例
POST / HTTP/1.1Host: postgres.tencentcloudapi.comContent-Type: application/jsonX-TC-Action: DescribeMem0Service<公共请求参数>{ "DBInstanceId": "postgres-pax**z0n"}
建议将该接口纳入自动化流程,作为部署 Agent 前的前置健康检查:Status 为 running 时取 InnerAddress 使用;为 creating 时等待1 – 2分钟后重查(开通通常3 – 5分钟内完成)。
八、获取服务地址
开通完成后,通过控制台或 DescribeMem0Service 云 API 查询服务详情,获取返回中的 InnerAddress(形如172.16.x.x:8000的内网地址);使用 Skill 部署时,也可以直接通过「获取 mem0 可用地址」指令查询。
这篇所有示例统一以 http://<服务地址> 表示该地址,请替换为实际值。服务地址为 VPC 内网地址,请确保调用方与实例网络互通(同 VPC 直连、云联网等)。
九、认证状态说明
注意:说明:
注意:记忆服务当前处于 needsSetup=true 状态,所有接口无需携带任何认证头即可访问,不需要 access_token,也不需要 API Key。你可以通过 GET /auth/setup-status 确认当前认证状态:
注意:curl -s http://<服务地址>/auth/setup-status# 返回{"needsSetup":true}
·当前版本不提供注册、登录类端点,调用时仅需携带 Content-Type: application/json。
·若后续服务完成初始化设置,认证将变为强制,届时需改用 API Key 或 Bearer Token 调用,请关注产品动态并及时调整调用方式。
注意:由于当前接口无需认证,任何可访问服务地址的调用方都能读写全部记忆数据。请务必保证服务仅在内网可达:通过安全组仅放通 Agent 所在网段,严禁将服务端口通过公网 CLB、NAT 映射或反向代理暴露到公网。
十、快速上手
本节用四个步骤带你走完一条完整的记忆链路:写入 → 检索 → 查看 → 删除。以下示例统一使用 curl,无需任何认证头。
十一、写入第一条记忆
提交一段对话,服务自动从中提取事实:
curl -s -X POST http://<服务地址>/memories \\ -H "Content-Type: application/json" \\ -d '{ "messages": [ {"role": "user", "content": "我叫张三,是一名后端工程师,喜欢用 Python。"} ], "user_id": "zhangsan" }'
预期返回(服务提取出事实,以 ADD 事件写入):
{ "results": [ {"id": "a1b2c3d4-…", "memory": "张三是一名后端工程师", "event": "ADD"}, {"id": "e5f6a7b8-…", "memory": "张三喜欢用 Python", "event": "ADD"} ]}
十二、语义检索记忆
用自然语言提问,通过 filters 限定范围、top_k 控制返回条数:
curl -s -X POST http://<服务地址>/search \\ -H "Content-Type: application/json" \\ -d '{"query": "张三用什么编程语言?", "filters": {"user_id": "zhangsan"}, "top_k": 3}'
{ "results": [ { "id": "e5f6a7b8-…", "memory": "张三喜欢用 Python", "score": 0.537, "user_id": "zhangsan", "attributed_to": "user" } ]}
十三、查看该用户的全部记忆
curl -s "http://<服务地址>/memories?user_id=zhangsan"
十四、删除记忆
# 删除单条(替换为实际 memory_id)curl -s -X DELETE http://<服务地址>/memories/a1b2c3d4-…# 返回{"message": "Memory deleted successfully"}
至此,你已完成一次完整的记忆读写闭环。接下来可以按需查阅各接口的详细说明。
十六、写入记忆
POST /memories
从对话中自动提取并存储值得记住的事实。请求体字段如下:
| 字段 | 类型 | 是否必填 | 说明 |
| messages | array | 是 | 对话消息数组,元素为 {"role": "…", "content": "…"} |
| user_id | string | 三选一(至少一个) | 用户维度 |
| agent_id | string | | Agent 维度 |
| run_id | string | | 运行实例维度 |
| metadata | object | 否 | 自定义元数据,便于业务侧标记与筛选 |
| infer | boolean | 否 | 是否使用大模型推断事实,默认 true;设为 false 时原文存储 |
| memory_type | string | 否 | 记忆类型标记,用于业务侧分类管理 |
| prompt | string | 否 | 自定义提取指令,引导模型按你的要求提取事实 |
写入时附加元数据与多维度的示例:
curl -s -X POST http://<服务地址>/memories \\ -H "Content-Type: application/json" \\ -d '{ "messages": [{"role": "user", "content": "客服场景:用户希望 24 小时内回复。"}], "user_id": "zhangsan", "agent_id": "customer-service", "metadata": {"source": "crm", "vip_level": "gold"} }'
十七、语义检索
POST /search
按语义相关度召回记忆,是将记忆注入 Prompt 的主要入口。请求体字段如下:
| 字段 | 类型 | 是否必填 | 说明 |
| query | string | 是 | 检索查询,支持自然语言 |
| filters | object | 否 | 检索范围过滤,如 {"user_id": "…", "agent_id": "…"},建议始终携带以缩小搜索域 |
| top_k | int | 否 | 返回条数,默认10 |
| threshold | float | 否 | 相似度阈值,低于该值的结果将被过滤,默认0.1 |
| explain | boolean | 否 | 是否返回评分详情,用于调试检索效果,详见 进阶能力 |
注意:说明:
注意:检索的返回条数参数为 top_k,范围过滤请使用 filters 对象(顶层直接传 user_id 的写法已废弃,请勿在新代码中使用)。
十八、查询记忆列表
GET/memories
按维度过滤返回记忆列表,支持 user_id、agent_id、run_id 三个查询参数(可组合):
# 某用户的全部记忆curl -s "http://<服务地址>/memories?user_id=zhangsan"# 某用户在某 Agent 下的记忆curl -s "http://<服务地址>/memories?user_id=zhangsan&agent_id=customer-service"
返回结构为 {"results": […]},每条记忆包含 id、memory、hash、metadata、created_at、updated_at、归属维度及 attributed_to(归属类型)等字段。
GET /memories/{memory_id} 可读取单条记忆的完整内容。
十九、更新与删除
PUT /memories/{memory_id} —— 修正某条记忆的文本:
curl -s -X PUT http://<服务地址>/memories/a1b2c3d4-… \\ -H "Content-Type: application/json" \\ -d '{"text": "用户是 VIP 金卡客户,偏好电话和邮件沟通"}'# 返回{"message": "Memory updated successfully!"}
DELETE /memories/{memory_id} —— 删除单条;DELETE /memories?user_id=… —— 清空某维度下全部记忆:
# 清空某用户的全部记忆curl -s -X DELETE "http://<服务地址>/memories?user_id=zhangsan"
注意:按维度清空记忆的请求不带 memory_id,一旦执行将删除该维度下所有记忆,请确认参数中的维度标识无误后再调用。
20、追溯变更历史
GET /memories/{memory_id}/history
返回某条记忆从创建到当前的完整变更轨迹,可用于审计与问题排查:
curl -s http://<服务地址>/memories/a1b2c3d4-…/history
每条历史记录包含 old_memory / new_memory(变更前后内容)、event(ADD / UPDATE / DELETE)、is_deleted 等字段,完整还原记忆的演变过程。
21、记忆的行为特性
理解以下服务行为,有助于你设计更合理的记忆写入与检索逻辑。
重复内容自动去重
服务通过内容哈希(hash)检测重复。提交完全相同的内容时不会重复存储,接口返回空结果 {"results":[]},这是正常现象,不代表写入失败。
infer=false:原文存储且不去重
写入时设置 "infer": false 将跳过大模型事实提取,消息原文原样存储,且不进行去重——相同内容多次提交会产生多条重复记忆。适用于需要保留对话原文的场景,请按需评估存储膨胀风险。
冲突信息采用 ADD-only 策略
遇到前后矛盾的信息时(例如先存“喜欢用 Python”,后存“改用 Go”),服务不会修改旧记忆,而是新增一条记录新事实,两条记忆并存,依靠时间维度区分新旧。应用侧如需“只保留最新事实”,可结合更新时间或变更历史自行处置旧记忆。
检索机制说明
当前版本启用的是语义向量检索,默认相似度阈值 threshold=0.1。通过 explain 模式可以看到 bm25_score(关键词匹配)与 entity_boost(实体加成)恒为 0,即关键词与实体信号暂未参与评分。
注意:说明:
注意:中文查询时,词面直接命中的记忆(如查询“张三”命中包含名字的记忆)可能比语义更相关的记忆得分更高,这是当前检索机制下的正常现象。如对排序效果有更高要求,可通过调整 threshold、优化 query 表述或在应用侧做二次排序来改善。
22、组织记忆的三种维度
合理使用隔离维度是设计记忆系统的关键。三种维度的典型定位如下:
| 维度 | 定位 | 典型用法 |
| user_id | 长期画像 | 记录终端用户的偏好、习惯、身份信息,伴随用户全生命周期。 |
| agent_id | 业务能力边界 | 同一用户在不同 Agent(客服、助手、代码评审)下的记忆互不串扰。 |
| run_id | 会话级暂存 | 记录某次任务执行过程的中间结论,任务结束后可整体清理。 |
常见组合模式:
·单 Agent 应用:仅使用 user_id,结构最简单。
·多 Agent 平台:user_id + agent_id 组合,实现“同一用户、不同 Agent 各有记忆”。
·任务型 Agent:在上述基础上叠加 run_id,将单次任务的临时记忆与长期记忆分离,任务结束后调用 DELETE /memories?run_id=… 清理。
维度之外,还可通过 metadata 为记忆附加业务标签(如来源系统、客户等级),在应用侧实现更细粒度的筛选逻辑。
23、接入你的 Agent
调通接口不等于 Agent 会用记忆。真正落地时需要回答一个问题:谁来决定什么时候读写记忆?记忆服务提供三种接入方式,对应三种答案。
24、接入方式总览
| 接入方式 | 读写时机由谁决定 | 适用 Agent 形态 | 改造成本 | 推荐度 |
| MCP 接入 | 大模型自主判断 | 支持 MCP 的现成客户端:Claude Code、Cursor、CodeBuddy 等 | 低(零业务代码,仅改配置) | 推荐 |
| Function Calling 接入 | 大模型自主判断 | 自研 Agent、已具备函数调用能力的 LLM 应用 | 中(注册2 – 3个工具) | 灵活 |
| REST API 直连 | 代码固定编排 | 对一致性有硬要求的生产 Agent,每轮必召回、必沉淀 | 中高(改造对话主循环) | 生产推荐 |
选型建议:
·使用现成客户端(IDE 编程助手、对话客户端)→ 选 MCP 接入。
·自研 Agent 且能接受“模型偶尔忘了查记忆”→ 选 Function Calling 接入。
·线上业务、要求每轮对话一定召回并沉淀 → 选 REST API 直连,在代码中固定编排;如需支持用户显式说“记住这个”,可叠加 Function Calling 的写入工具。
25、通过 MCP 接入
MCP(Model Context Protocol)是 Agent 客户端接入外部工具的通用协议。接入后,记忆读写变成 Agent 可自动发现和调用的“工具”。由于服务无需认证,配置只需一项核心信息:服务地址。
通用 MCP 配置
JSON
{ "mcpServers": { "tencent-memory": { "url": "http://<服务地址>/mcp", "type": "streamable-http" } }}
| 字段 | 说明 |
| url | MCP 端点,格式 http://<服务地址>/mcp(注意包含 /mcp 路径) |
| type | 传输类型 streamable-http(Streamable HTTP,基于 JSON-RPC 2.0)。各客户端对类型字段的写法不完全统一(如 http、streamable-http),请以所用客户端的文档为准 |
注意:说明:
注意:当前服务未启用认证,MCP 配置中无需填写 headers 或任何凭证。安全保障依赖内网地址与网络访问控制。
各客户端配置位置
| 客户端 | 配置方式 | 说明 |
| Claude Code | 项目根目录 .mcp.json,或执行 claude mcp add | 团队共享可提交到代码仓库 |
| Cursor | .cursor/mcp.json 或 MCP 配置界面 | 编辑器内置 MCP 管理 |
| CodeBuddy | MCP 配置界面 | 可视化添加 streamable-http 服务器 |
注意:MCP 配置不会热加载。修改配置后请完全重启客户端或新建会话(GUI 类客户端需彻底退出后再启动),否则 Agent 看不到记忆工具。
MCP 工具清单
MCP 暴露的工具与 REST 端点一一对应,Agent 无需理解 HTTP 细节:
| MCP 工具 | 对应 REST 端点 | 功能 |
| add_memory | POST /memories | 创建记忆(自动提取事实) |
| search_memories | POST /search | 语义检索记忆 |
| get_memories | GET /memories | 获取记忆列表 |
| get_memory | GET /memories/{id} | 获取单条记忆 |
| update_memory | PUT /memories/{id} | 更新单条记忆 |
| delete_memory | DELETE /memories/{id} | 删除单条记忆 |
| delete_all_memories | DELETE /memories | 删除某维度下全部记忆 |
| get_memory_history | GET /memories/{id}/history | 获取记忆变更历史 |
| reset_memory | POST /reset | 重置全部记忆(危险操作) |
验证接入
配置生效后,在对话中直接测试:
1. 发送:请记住:我的项目数据库是云数据库 PostgreSQL,生产环境在广州地域。 若 Agent 调用了记忆写入工具并成功返回,说明接入完成。
2. 新开一个会话,追问:我的项目数据库是什么? Agent 应能通过检索记忆给出正确答案——这验证了跨会话记忆闭环。
让 Agent 主动使用记忆
MCP 只解决“能不能调”,不解决“愿不愿调”。默认情况下模型往往只在用户明确说“记住”时才写记忆。要做到自动召回与自动沉淀,建议在项目规则文件(如 AGENTS.md 或客户端的自定义指令)中补充约束:
AGENTS.md 示例
## 长时记忆使用规约本项目已接入 tencent-memory 记忆服务,请严格遵循:1. 回答前先召回:当问题涉及项目约定、技术选型、个人偏好或历史决策时, 先调用 search_memories 检索相关记忆,再组织回答;不要凭空假设。2. 形成结论后沉淀:确定技术选型、明确表达偏好、排查出根因时, 调用 add_memory 记录一条简洁事实。3. 一条记忆只讲一件事:使用陈述句,避免写入大段过程描述。4. 结论变化时先纠正:发现旧记忆已过时,先定位记忆 ID, 再用 update_memory 更新或 delete_memory 删除,不要直接追加矛盾记忆。5. 禁止写入敏感信息:密码、密钥、证书、身份证号等一律不得写入记忆。
26、通过 Function Calling 接入
自研 Agent 通常已具备 Function Calling 能力,此时无需引入 MCP,把记忆能力注册为工具即可。工具描述直接决定模型的调用准确率——关键技巧是在 description 中同时写清“什么时候调用”和“什么时候不要调用”:
工具声明示例(JSON Schema)
[ { "type": "function", "function": { "name": "recall_memory", "description": "检索用户的长期记忆。当问题涉及用户身份、偏好、历史决策、项目约定,或出现「我之前说过」「还记得吗」等指代时必须调用。纯计算、纯代码生成类问题不要调用。", "parameters": { "type": "object", "properties": { "query": {"type": "string", "description": "检索语句,建议保留用户原话中的关键实体"}, "top_k": {"type": "integer", "description": "返回条数", "default": 5} }, "required": ["query"] } } }, { "type": "function", "function": { "name": "remember_fact", "description": "把一条对后续会话仍有价值的事实写入长期记忆。仅在用户表达稳定偏好、确认技术方案、给出长期约束时调用。寒暄、临时数值、中间结果不要写入。禁止写入密钥等敏感信息。", "parameters": { "type": "object", "properties": { "content": {"type": "string", "description": "一句话陈述句,只描述一件事"}, "metadata": {"type": "object", "description": "可选业务标签"} }, "required": ["content"] } } }]
工具的内部实现即调用 POST /search 与 POST /memories,可复用下文 REST 直连中的客户端封装。
27、通过 REST API 直连
生产环境的推荐集成模式为三步闭环:检索记忆 → 生成回复 → 回写记忆,由代码固定编排,保证每轮对话一定会召回、一定会沉淀。
PythonNode.jsJavaGoPHP完整可运行示例import requestsBASE_URL = "http://<服务地址>" # 替换为 DescribeMem0Service 返回的 InnerAddressclass MemoryClient: """Agent 记忆服务客户端封装(当前版本无需认证头)""" def __init__(self, base_url: str): self.base_url = base_url.rstrip("/") self.headers = {"Content-Type": "application/json"} def add(self, messages, user_id=None, agent_id=None, run_id=None, metadata=None): payload = {"messages": messages} if user_id: payload["user_id"] = user_id if agent_id: payload["agent_id"] = agent_id if run_id: payload["run_id"] = run_id if metadata: payload["metadata"] = metadata r = requests.post(f"{self.base_url}/memories", json=payload, headers=self.headers) r.raise_for_status() return r.json() def search(self, query, user_id=None, agent_id=None, top_k=5, threshold=None): filters = {} if user_id: filters["user_id"] = user_id if agent_id: filters["agent_id"] = agent_id payload = {"query": query, "top_k": top_k, "filters": filters} if threshold is not None: payload["threshold"] = threshold r = requests.post(f"{self.base_url}/search", json=payload, headers=self.headers) r.raise_for_status() return r.json()["results"]def chat_with_memory(client: MemoryClient, message: str, user_id: str): # 1. 检索相关记忆 memories = client.search(message, user_id=user_id, top_k=3) memory_context = "\\n".join(f"- {m['memory']}" for m in memories) # 2. 将记忆注入 Prompt,调用你的大模型 # prompt = f"以下是用户的历史记忆:\\n{memory_context}\\n\\n用户说:{message}" # reply = your_llm_call(prompt) reply = "(此处替换为大模型生成的回复)" # 3. 回写本轮对话,沉淀新记忆(生产环境建议异步执行,见下文工程原则) client.add( [ {"role": "user", "content": message}, {"role": "assistant", "content": reply}, ], user_id=user_id, ) return replyif __name__ == "__main__": client = MemoryClient(BASE_URL) client.add( [{"role": "user", "content": "我叫张三,是一名后端工程师,喜欢用 Python。"}], user_id="zhangsan", ) chat_with_memory(client, "你还记得我是做什么工作的吗?", user_id="zhangsan")const BASE_URL = "http://<服务地址>";const headers = { "Content-Type": "application/json" };// 写入记忆async function addMemory(messages, userId) { const res = await fetch(`${BASE_URL}/memories`, { method: "POST", headers, body: JSON.stringify({ messages, user_id: userId }), }); return res.json();}// 检索记忆(注意使用 filters + top_k)async function searchMemory(query, userId) { const res = await fetch(`${BASE_URL}/search`, { method: "POST", headers, body: JSON.stringify({ query, filters: { user_id: userId }, top_k: 5 }), }); return (await res.json()).results;}JDK 15+(文本块语法),无需第三方依赖import java.net.URI;import java.net.http.HttpClient;import java.net.http.HttpRequest;import java.net.http.HttpResponse;public class MemoryDemo { static final String BASE_URL = "http://<服务地址>"; public static void main(String[] args) throws Exception { HttpClient client = HttpClient.newHttpClient(); // 写入记忆 String addBody = """ {"messages":[{"role":"user","content":"我叫张三,是一名后端工程师。"}],"user_id":"zhangsan"}"""; HttpRequest addReq = HttpRequest.newBuilder() .uri(URI.create(BASE_URL + "/memories")) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(addBody)) .build(); System.out.println(client.send(addReq, HttpResponse.BodyHandlers.ofString()).body()); // 检索记忆(注意使用 filters + top_k) String searchBody = """ {"query":"张三的职业","filters":{"user_id":"zhangsan"},"top_k":3}"""; HttpRequest searchReq = HttpRequest.newBuilder() .uri(URI.create(BASE_URL + "/search")) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(searchBody)) .build(); System.out.println(client.send(searchReq, HttpResponse.BodyHandlers.ofString()).body()); }}仅使用标准库package mainimport ( "bytes" "fmt" "io" "net/http")const baseURL = "http://<服务地址>"func post(path, body string) string { resp, err := http.Post(baseURL+path, "application/json", bytes.NewBufferString(body)) if err != nil { panic(err) } defer resp.Body.Close() data, _ := io.ReadAll(resp.Body) return string(data)}func main() { // 写入记忆 fmt.Println(post("/memories", `{"messages":[{"role":"user","content":"我叫张三,是一名后端工程师。"}],"user_id":"zhangsan"}`)) // 检索记忆(注意使用 filters + top_k) fmt.Println(post("/search", `{"query":"张三的职业","filters":{"user_id":"zhangsan"},"top_k":3}`))}依赖 cURL 扩展<?php$baseUrl = "http://<服务地址>";function post($url, $payload) { $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => ["Content-Type: application/json"], CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE), ]); $result = curl_exec($ch); curl_close($ch); return $result;}// 写入记忆echo post("$baseUrl/memories", [ "messages" => [["role" => "user", "content" => "我叫张三,是一名后端工程师。"]], "user_id" => "zhangsan",]);// 检索记忆(注意使用 filters + top_k)echo post("$baseUrl/search", [ "query" => "张三的职业", "filters" => ["user_id" => "zhangsan"], "top_k" => 3,]);
28、记忆注入的工程原则
无论采用哪种接入方式,将召回的记忆注入 Prompt 时都建议遵循以下原则:
| 原则 | 做法 | 原因 |
| 放 system 段,不放 user 消息 | 记忆块拼进 system prompt | 避免模型把历史事实误当成本轮新诉求 |
| 声明时效优先级 | 显式写明“与本轮陈述冲突时以本轮为准” | 服务采用 ADD-only 策略,同一主题可能并存新旧事实 |
| 控制注入量 | 用 top_k + threshold 限制条数与相关度 | 注入过多低相关记忆会稀释上下文、推高 token 成本 |
| 沉淀异步化 | 写入操作放后台线程/队列执行 | 写入需经大模型提取,耗时明显高于检索,同步执行会拖慢每轮响应 |
| 召回失败可降级 | 检索异常时本轮按无记忆处理并记录日志 | 记忆是增强能力,不应成为对话主链路的单点故障 |
30、检索解释模式
在检索请求中设置 explain: true,每条返回的记忆会附带 score_details 字段,展示各评分信号明细,便于调优检索效果:
curl -s -X POST http://<服务地址>/search \\ -H "Content-Type: application/json" \\ -d '{"query": "用户的饮食偏好", "filters": {"user_id": "zhangsan"}, "top_k": 3, "explain": true}'
score_details 包含 semantic_score(语义相似度)、bm25_score(关键词匹配,当前恒为 0)、entity_boost(实体加成,当前恒为 0)、final_score(综合评分)与生效的 threshold。
31、自定义记忆提取指令
POST /generate-instructions 可根据你的业务场景描述,自动生成一套记忆提取指令(custom_instructions)及配套测试消息,用于定制事实提取的侧重点:
curl -s -X POST http://<服务地址>/generate-instructions \\ -H "Content-Type: application/json" \\ -d '{"use_case": "customer support chatbot that remembers user preferences"}'
32、实体管理与级联清理
GET /entities 可列出全部 user_id / agent_id / run_id 及其记忆数量,用于盘点记忆分布;DELETE /entities/{entity_type}/{entity_id} 可级联删除某实体下的全部记忆(entity_type 取值为 user、agent、run):
# 盘点记忆分布curl -s http://<服务地址>/entities# 用户注销后清理其全部记忆curl -s -X DELETE "http://<服务地址>/entities/user/zhangsan"
33、请求日志
GET /requests 可查看服务收到的请求记录,便于排查调用链路与审计。
34、重置全部记忆
POST /reset 会清空服务上的所有记忆。
注意:警告:
注意:/reset 与不带维度的 DELETE /memories 属于高危操作,执行后数据不可恢复。请在调用前进行二次确认,并通过网络层(安全组)限制可发起此类调用的来源。
35、安全与运维建议
1. 网络隔离优先:当前版本接口无需认证,服务地址严禁暴露公网。安全组仅放通 Agent 所在网段或安全组,不要使用 0.0.0.0/0。
2. 维度必传:检索与删除操作务必携带明确的维度参数(filters / 查询参数),防止越权读取或误删其他用户的数据。
3. 危险操作管控:对 /reset、按维度清空、级联删除等操作建立审批或二次确认机制。
4. 敏感数据评估:记忆中可能包含用户画像与业务决策,写入前请评估数据敏感性,避免写入密码、密钥等凭证类信息。
5. 审计追溯:结合请求日志(GET /requests)、记忆变更历史接口与云数据库 PostgreSQL 的数据库审计能力,满足合规审计要求。
6. 定期治理:对长期未命中的记忆、临时 run_id 维度的记忆定期清理,控制存储规模并保持检索质量。
7. 关注认证状态变化:服务完成初始化后认证将转为强制,请通过 GET /auth/setup-status 关注状态,提前规划调用方式切换。
36、错误码速查
| 状态码 | 含义 | 处理建议 |
| 400 | 请求参数错误 | 检查请求体字段是否完整:写入时至少提供一个隔离维度;检索时使用 top_k 与 filters 而非旧参数名。 |
| 404 | 资源不存在 | 检查 memory_id 是否正确,记忆可能已被删除。 |
| 409 | 资源冲突 | 按返回信息调整请求内容后重试。 |
| 500 | 服务端错误 | 稍后重试;持续出现时请检查模型服务配置(如 Embedding Key 是否有效),或 提交工单 处理。 |
注意:说明:
注意:当前服务处于 needsSetup=true 状态,接口不做身份校验,因此不会返回 401 / 403。若调用失败,请优先排查网络连通性(安全组、VPC)而非凭证问题。
38、Q:写入记忆后返回 {"results":[]},是失败了吗?
A:不是。这表示提交的内容与已有记忆完全重复,服务通过内容哈希自动去重,未产生新记忆。可通过 GET /memories?user_id=… 确认记忆是否已存在。
39、Q:写入记忆后,检索不到如何处理?
A:请确认如下情况并参考处理。
·确认检索时 filters 中的维度标识与写入时一致,记忆仅在所属维度内可被检索。
·默认相似度阈值为0.1,若显式设置了更高的 threshold,可适当降低后再试。
·注意检索参数为 top_k 和 filters 对象,使用旧参数名(limit、顶层 user_id)可能不生效。
40、Q:配置了 MCP,但 Agent 看不到记忆工具如何处理?
A:请确认如下情况并参考处理。
·确认配置中的 url 包含 /mcp 路径,且服务地址从当前客户端环境可达。
·MCP 配置不会热加载,修改后请完全重启客户端或新建会话。
·各客户端对 type 字段的取值写法不同(streamable-http / http),请以客户端文档为准。
41、Q:Agent 接入了记忆工具,但很少主动调用?
A:这是模型行为问题而非接入问题。请在项目规则文件或系统提示词中加入明确的使用规约(何时召回、何时沉淀、禁止写入敏感信息),可参考 让 Agent 主动使用记忆 一节的示例。
42、Q:用户更新了偏好,为什么旧记忆还在?
A:服务对冲突信息采用 ADD-only 策略:新事实以新增方式记录,旧记忆保留。如需清理,可根据变更历史或更新时间定位旧记忆后调用删除接口。
43、Q:用户注销账号后,如何清理其全部记忆?
A:调用 DELETE /entities/user/<user_id> 级联删除该用户维度下的全部记忆,满足数据合规要求。
44、Q:如何验证服务是否正常可用?
A:按顺序执行三步验证:
1. GET / 返回 HTTP 200,确认服务在线(网络层)。
2. GET /configure 返回配置信息,确认接口可用(应用层)。
3. 写入一条测试记忆再删除(可用专用 user_id 如 __healthcheck__),确认读写链路与模型调用正常。
在线接口文档可访问 http://<服务地址>/docs 查看,支持交互式测试。
45、相关文档
·Agent 长期记忆功能概述
·多 Agent 共享记忆与项目协作
·基于 OpenClaw 构建 AI Agent 应用