Agent 记忆服务是云数据库 PostgreSQL 为 AI 应用提供的托管式长期记忆能力。本文介绍如何通过 REST API 完成记忆的写入、检索、更新与删除,并以 MCP、Function Calling、REST 直连三种方式将记忆能力接入您的 Agent 应用。
功能简介
什么是 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 |
说明:
开启指定实例的记忆服务。
输入参数
参数 | 必填 | 类型 | 说明 |
DBInstanceId | 是 | String | PostgreSQL 实例 ID,如 postgres-paxanz0n |
AgenticBaseId | 是 | String | AgenticBase 套餐 ID,如 agenticbase-2a7vajd1(需先开通) |
LLMModel | 是 | String | 用于事实提取的 LLM,可选值见下方模型列表 |
EmbeddingApiKey | 是 | String |
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,一旦执行将删除该维度下所有记忆,请确认参数中的维度标识无误后再调用。
追溯变更历史
GET /memories/{memory_id}/history
返回某条记忆从创建到当前的完整变更轨迹,可用于审计与问题排查:
curl -s http://<服务地址>/memories/a1b2c3d4-.../history
每条历史记录包含 old_memory / new_memory(变更前后内容)、event(ADD / UPDATE / DELETE)、is_deleted 等字段,完整还原记忆的演变过程。
记忆的行为特性
理解以下服务行为,有助于您设计更合理的记忆写入与检索逻辑。
重复内容自动去重
服务通过内容哈希(hash)检测重复。提交完全相同的内容时不会重复存储,接口返回空结果 {"results":[]},这是正常现象,不代表写入失败。
infer=false:原文存储且不去重
写入时设置 "infer": false 将跳过大模型事实提取,消息原文原样存储,且不进行去重——相同内容多次提交会产生多条重复记忆。适用于需要保留对话原文的场景,请按需评估存储膨胀风险。
冲突信息采用 ADD-only 策略
遇到前后矛盾的信息时(例如先存“喜欢用 Python”,后存“改用 Go”),服务不会修改旧记忆,而是新增一条记录新事实,两条记忆并存,依靠时间维度区分新旧。应用侧如需“只保留最新事实”,可结合更新时间或变更历史自行处置旧记忆。
检索机制说明
当前版本启用的是语义向量检索,默认相似度阈值 threshold=0.1。通过 explain 模式可以看到 bm25_score(关键词匹配)与 entity_boost(实体加成)恒为 0,即关键词与实体信号暂未参与评分。
说明:
中文查询时,词面直接命中的记忆(如查询“张三”命中包含名字的记忆)可能比语义更相关的记忆得分更高,这是当前检索机制下的正常现象。如对排序效果有更高要求,可通过调整 threshold、优化 query 表述或在应用侧做二次排序来改善。
组织记忆的三种维度
合理使用隔离维度是设计记忆系统的关键。三种维度的典型定位如下:
维度 | 定位 | 典型用法 |
user_id | 长期画像 | 记录终端用户的偏好、习惯、身份信息,伴随用户全生命周期。 |
agent_id | 业务能力边界 | 同一用户在不同 Agent(客服、助手、代码评审)下的记忆互不串扰。 |
run_id | 会话级暂存 | 记录某次任务执行过程的中间结论,任务结束后可整体清理。 |
常见组合模式:
单 Agent 应用:仅使用 user_id,结构最简单。
多 Agent 平台:user_id + agent_id 组合,实现“同一用户、不同 Agent 各有记忆”。
任务型 Agent:在上述基础上叠加 run_id,将单次任务的临时记忆与长期记忆分离,任务结束后调用 DELETE /memories?run_id=... 清理。
维度之外,还可通过 metadata 为记忆附加业务标签(如来源系统、客户等级),在应用侧实现更细粒度的筛选逻辑。
接入您的 Agent
调通接口不等于 Agent 会用记忆。真正落地时需要回答一个问题:谁来决定什么时候读写记忆?记忆服务提供三种接入方式,对应三种答案。
接入方式总览
接入方式 | 读写时机由谁决定 | 适用 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 的写入工具。
通过 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. 禁止写入敏感信息:密码、密钥、证书、身份证号等一律不得写入记忆。
通过 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 直连中的客户端封装。
通过 REST API 直连
生产环境的推荐集成模式为三步闭环:检索记忆 → 生成回复 → 回写记忆,由代码固定编排,保证每轮对话一定会召回、一定会沉淀。
完整可运行示例
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_idif agent_id: payload["agent_id"] = agent_idif run_id: payload["run_id"] = run_idif metadata: payload["metadata"] = metadatar = 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_idif agent_id: filters["agent_id"] = agent_idpayload = {"query": query, "top_k": top_k, "filters": filters}if threshold is not None: payload["threshold"] = thresholdr = 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,]);
记忆注入的工程原则
无论采用哪种接入方式,将召回的记忆注入 Prompt 时都建议遵循以下原则:
原则 | 做法 | 原因 |
放 system 段,不放 user 消息 | 记忆块拼进 system prompt | 避免模型把历史事实误当成本轮新诉求 |
声明时效优先级 | 显式写明“与本轮陈述冲突时以本轮为准” | 服务采用 ADD-only 策略,同一主题可能并存新旧事实 |
控制注入量 | 用 top_k + threshold 限制条数与相关度 | 注入过多低相关记忆会稀释上下文、推高 token 成本 |
沉淀异步化 | 写入操作放后台线程/队列执行 | 写入需经大模型提取,耗时明显高于检索,同步执行会拖慢每轮响应 |
召回失败可降级 | 检索异常时本轮按无记忆处理并记录日志 | 记忆是增强能力,不应成为对话主链路的单点故障 |
进阶能力
检索解释模式
在检索请求中设置 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。
自定义记忆提取指令
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"}'
实体管理与级联清理
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"
请求日志
GET /requests 可查看服务收到的请求记录,便于排查调用链路与审计。
重置全部记忆
POST /reset 会清空服务上的所有记忆。
警告:
/reset 与不带维度的 DELETE /memories 属于高危操作,执行后数据不可恢复。请在调用前进行二次确认,并通过网络层(安全组)限制可发起此类调用的来源。
安全与运维建议
1. 网络隔离优先:当前版本接口无需认证,服务地址严禁暴露公网。安全组仅放通 Agent 所在网段或安全组,不要使用 0.0.0.0/0。
2. 维度必传:检索与删除操作务必携带明确的维度参数(filters / 查询参数),防止越权读取或误删其他用户的数据。
3. 危险操作管控:对 /reset、按维度清空、级联删除等操作建立审批或二次确认机制。
4. 敏感数据评估:记忆中可能包含用户画像与业务决策,写入前请评估数据敏感性,避免写入密码、密钥等凭证类信息。
5. 审计追溯:结合请求日志(GET /requests)、记忆变更历史接口与云数据库 PostgreSQL 的数据库审计能力,满足合规审计要求。
6. 定期治理:对长期未命中的记忆、临时 run_id 维度的记忆定期清理,控制存储规模并保持检索质量。
7. 关注认证状态变化:服务完成初始化后认证将转为强制,请通过 GET /auth/setup-status 关注状态,提前规划调用方式切换。
错误码速查
状态码 | 含义 | 处理建议 |
400 | 请求参数错误 | 检查请求体字段是否完整:写入时至少提供一个隔离维度;检索时使用 top_k 与 filters 而非旧参数名。 |
404 | 资源不存在 | 检查 memory_id 是否正确,记忆可能已被删除。 |
409 | 资源冲突 | 按返回信息调整请求内容后重试。 |
500 | 服务端错误 |
说明:
当前服务处于 needsSetup=true 状态,接口不做身份校验,因此不会返回 401 / 403。若调用失败,请优先排查网络连通性(安全组、VPC)而非凭证问题。
常见问题
Q:写入记忆后返回 {"results":[]},是失败了吗?
A:不是。这表示提交的内容与已有记忆完全重复,服务通过内容哈希自动去重,未产生新记忆。可通过 GET /memories?user_id=... 确认记忆是否已存在。
Q:写入记忆后,检索不到如何处理?
A:请确认如下情况并参考处理。
确认检索时 filters 中的维度标识与写入时一致,记忆仅在所属维度内可被检索。
默认相似度阈值为0.1,若显式设置了更高的 threshold,可适当降低后再试。
注意检索参数为 top_k 和 filters 对象,使用旧参数名(limit、顶层 user_id)可能不生效。
Q:配置了 MCP,但 Agent 看不到记忆工具如何处理?
A:请确认如下情况并参考处理。
确认配置中的 url 包含 /mcp 路径,且服务地址从当前客户端环境可达。
MCP 配置不会热加载,修改后请完全重启客户端或新建会话。
各客户端对 type 字段的取值写法不同(streamable-http / http),请以客户端文档为准。
Q:Agent 接入了记忆工具,但很少主动调用?
Q:用户更新了偏好,为什么旧记忆还在?
A:服务对冲突信息采用 ADD-only 策略:新事实以新增方式记录,旧记忆保留。如需清理,可根据变更历史或更新时间定位旧记忆后调用删除接口。
Q:用户注销账号后,如何清理其全部记忆?
A:调用 DELETE /entities/user/<user_id> 级联删除该用户维度下的全部记忆,满足数据合规要求。
Q:如何验证服务是否正常可用?
A:按顺序执行三步验证:
1. GET / 返回 HTTP 200,确认服务在线(网络层)。
2. GET /configure 返回配置信息,确认接口可用(应用层)。
3. 写入一条测试记忆再删除(可用专用 user_id 如 __healthcheck__),确认读写链路与模型调用正常。
在线接口文档可访问 http://<服务地址>/docs 查看,支持交互式测试。