ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

AgentScope 集成 mem0 长期记忆中间件:跨会话记忆的三种控制模式与底层适配原理

2026/9/10 16:08:31 拓冰建站 浏览量
AgentScope 集成 mem0 长期记忆中间件:跨会话记忆的三种控制模式与底层适配原理 AgentScope 集成 mem0 长期记忆中间件跨会话记忆的三种控制模式与底层适配原理【免费下载链接】agentscopeBuild and run agents you can see, understand and trust.项目地址: https://gitcode.com/GitHub_Trending/ag/agentscopeMem0Middleware 是 AgentScope 提供的长期记忆中间件它把开源 mem0 与对应源码完整讲解其安装、三种构造路径、static_control / agent_control / both 三种控制模式、跨 Agent 共享与记忆隔离scoping并深入_agentscope_adapter.py揭示用 AgentScope 自己的大模型驱动 mem0 抽取与向量化的底层实现最终带读者读懂可运行的 oss_demo.py。为什么需要 mem0 中间件Agent 会话级记忆的缺口普通 Agent 每次会话的上下文是临时的会话结束后模型既没有记住用户说过的偏好也无法在下一次会话中主动回忆。AgentScope 的中间件机制MiddlewareBase恰好提供了在on_reply前后、系统提示词构建时、工具列表中挂钩的能力mem0 则负责记忆的持久化存储、抽取与向量检索。二者结合后用户在第 1 轮告诉 Agent做图表默认用暗色模式、matplotlib我住在杭州第 2 轮开启一个全新的 Agent 实例空上下文直接问给我画月度销售额柱状图Agent 无需任何提示就能自动检索到上轮记忆自主选择暗色主题与 matplotlib。这正是 oss_demo.py 要演示的核心效果。整个链路不引入独立于 AgentScope 之外的第二套模型客户端记忆抽取LLM与向量化Embedding全部复用 AgentScope 已有的模型示例中使用 DashScope 的通义千问与 text-embedding因此不需要为 mem0 单独配置 OpenAI key。安装与依赖mem0 是 AgentScope 的可选依赖通过 extra 安装即可# 等价于 pip install agentscope mem0ai2.0.0,3.0.0 pip install agentscope[memory-mem0]依赖约束可以在 pyproject.toml 中核实memory-mem0 [mem0ai2.0.0]且该 extra 同时被聚合进mem0组合 extra。运行 OSS 路径的 demo 需要配置 DashScope 密钥若切换到 hosted mem0 Platform 则需平台密钥export DASHSCOPE_API_KEYsk-... # OSS 路径 # Platform 路径仅在切换时 # export MEM0_API_KEYm0-... # export OPENAI_API_KEYsk-... # 仅当你的 Agent 聊天模型是 OpenAI 时才需要导入路径Mem0Middleware从 middleware 包顶层导出见 middleware/init.py同时需要Toolkit来挂载记忆工具from agentscope.middleware import Mem0Middleware from agentscope.tool import Toolkit三种构造路径与参数优先级Mem0Middleware的构造签名见 _middleware.py为Mem0Middleware( *, user_id: str, # 必填记忆命名空间 clientNone, # 预构建的 mem0 异步客户端 chat_modelNone, # AgentScope ChatModelBase embedding_modelNone, # AgentScope EmbeddingModelBase mem0_configNone, # mem0 MemoryConfig自定义向量库/历史库/reranker modeboth, # static_control / agent_control / both agent_idNone, # 默认取 agent.name top_k5, # 每次检索的最大条数 thresholdNone, # 最低相似度阈值None 交给 mem0 scope_search_by_agentTrue, # 检索时是否按 agent_id 过滤 await_writeTrue, # 写回是否同步等待 memory_section_header..., # 注入记忆的标题文案 memory_section_intro..., # 注入记忆的引导文案 tool_instructions..., # agent_control/both 模式的系统提示增强 )README 归纳的三种合法构造方式如下# 1. Models —— 构建本地 OSS AsyncMemoryLLM/Embedding 走 AgentScope 模型 Mem0Middleware( user_idalice, chat_modelmy_chat_model, embedding_modelmy_embedding_model, modeboth, ) # 2. Models 自定义 mem0_config —— 保留自定义向量库/历史库/reranker # 仅覆盖 .llm 与 .embedder 两个槽位为 AgentScope 适配器 Mem0Middleware( user_idalice, chat_modelmy_chat_model, embedding_modelmy_embedding_model, mem0_configMemoryConfig( vector_storeVectorStoreConfig( providerqdrant, config{host: my-qdrant, port: 6333}, ), history_db_path/data/mem0_history.db, ), modeboth, ) # 3. Client —— 自带预构建客户端完全掌控 mem0 装配 # OSS 后端 Mem0Middleware(user_idalice, clientAsyncMemory(), modeboth) # 托管 Platform 后端 Mem0Middleware(user_idalice, clientAsyncMemoryClient(api_keym0-...), modeboth)优先级与校验矩阵_resolve_client_middleware.py实现了严格的后端解析逻辑README 给出了完整矩阵clientmem0_configchat_modelembedding_model行为✓———直接使用client。✓anyanyany使用client其余三个参数被忽略并打印WARNING日志列出被丢弃的 kwargs。—✓——将mem0_config包装进AsyncMemory无覆盖。—✓✓—包装并仅用 AgentScope 适配器覆盖.llm保留mem0_config的.embedder。—✓—✓包装并仅覆盖.embedder保留.llm。—✓✓✓包装并同时覆盖.llm与.embedder其余字段保留。——✓✓新建默认MemoryConfigmem0 默认向量库/历史库接入 AgentScope 适配器。——✓—❌ValueError—— 省略mem0_config时chat_model与embedding_model必须成对出现。———✓❌ 同上。————❌ValueError—— 三者必须提供其一。两个设计动机值得注意client绝对优先同一个Mem0Middleware(...)调用既能服务库内用户传 AgentScope 模型也能服务生产部署传预构建client。被忽略的 kwargs 通过WARNING日志显式暴露不会静默丢失。config 覆盖机制可以维护一份规范的MemoryConfig模板自定义向量库、历史库、reranker 等在每个调用点只通过chat_model/embedding_model替换 LLM 与 Embedder实现模板复用、按点切换。此外_resolve_client还会校验客户端必须是异步的mem0.AsyncMemoryOSS或mem0.AsyncMemoryClientPlatform均可同步版Memory/MemoryClient会在构造期直接抛出TypeError。检测逻辑_looks_async使用inspect.unwrap剥开 mem0 Platform 客户端上api_error_handler这类同步functools.wraps包装避免把被同步装饰器包住的 async 方法误判为同步该行为有专门的单测覆盖见 mem0_middleware_test.py。三种控制模式由谁来决定何时读写记忆mode参数决定 Agent 与 mem0 的交互方式核心区别在于模型能看到什么与什么会自动触发static_control中间件全权代劳Agent 无感知该模式复刻了 AgentScope 1.x 中ReActAgent._retrieve_from_long_term_memory的行为流程见on_reply_middleware.py为on_reply前用最新用户消息查询 mem0预取检索结果ReplyStartEvent时机注入该事件在 Agent 将新用户输入写入state.context之后、推理循环开始之前触发。中间件此时把一个AssistantMsg(namememory, ...)追加到state.context使记忆注记紧跟在用户新消息之后与 v1 在self.memory.add(msg)之后的插入位置一致测试 test_memory_message_lands_after_user_message 专门校验了这一顺序on_reply后把本轮(user, assistant)对话写回 mem0。注入的记忆消息会持久保留在上下文中长会话每轮检索到内容就会累积一条。如果担心 token 膨胀可用compress_context后处理或自写中间件将其弹出。注_extract_query_text_utils.py会跳过ExternalExecutionResultEvent、UserConfirmResultEvent这类 HITL 恢复事件——恢复轮不触发检索也不触发写回。agent_controlAgent 自主决定工具驱动中间件只暴露两个工具——search_memory(keywords, limit)与add_memory(thinking, content)其余完全旁观无自动检索、无自动写回。构造 Agent 时需要显式把工具挂进 toolkit中间件自身不会修改 toolkit测试 test_middleware_does_not_mutate_toolkit 印证了这一点mw Mem0Middleware(..., modeagent_control) agent Agent( ..., toolkitToolkit(toolsawait mw.list_tools()), middlewares[mw], )系统提示词会追加一小段引导DEFAULT_TOOL_INSTRUCTIONS通过on_system_prompt注入_middleware.py提示记忆工具存在每个工具的具体用法由标准 tool schema 承载。两个工具的 schema 与实现见 _tools.pysearch_memory(keywords: list[str], limit: int 5)多关键词并行检索结果合并去重后返回失败时返回stateERROR的ToolChunk由 toolkit 聚合为失败的工具调用。add_memory(thinking: str, content: list[str])content逐条作为独立完整句写入 mem0thinkingAgent 的记忆理由不会进入 mem0——它只出现在工具返回文本中用于审计避免 mem0 的记忆被 Agent 的自我叙述污染测试 test_add_memory_does_not_persist_thinking。写路径采用两层兜底策略_async_add_with_fallback_middleware.py先让 mem0 的抽取 LLM 正常抽取若抽取结果为空则改用inferFalse把原文直接入库保证add_memory调用永远至少存下点什么。注释详细解释了为何从 v1 的三层降为两层——mem0 v2.x 按 filters 而非消息角色选择抽取提示词v1 的切换 assistant 角色重试已退化为一次无意义的 LLM 调用测试 test_add_memory_two_tier_fallback 验证了恰好两次add调用且均使用 user 角色。这两个工具继承自_Mem0MemoryToolBasecheck_permissions返回PermissionBehavior.ALLOW自动放行——记忆工具属于 Agent 的标准能力每次调用都弹权限确认反而失去意义测试 test_tools_auto_allow_permission。both默认双轨并行静态检索注入与按需工具同时生效记忆被自动检索为上下文中的 assistant 注记同时search_memory/add_memory工具含系统提示引导对 Agent 开放。这与 AgentScope 1.x 中ReActAgent.long_term_memory_mode的默认值一致也是 oss_demo.py 采用的模式MODE both。跨 Agent 共享中间件与 Qdrant 独占锁本地 OSS mem0 后端默认使用磁盘上的 Qdrant而 Qdrant 对存储目录默认/tmp/qdrant持有独占锁。若两个Mem0Middleware各自从chat_modelembedding_model构建会各自创建自己的AsyncMemory第二个实例将崩溃RuntimeError: Storage folder /tmp/qdrant is already accessed by another instance of Qdrant client.解决方案是构建一个Mem0Middleware实例并传给所有需要共享同一记忆命名空间的 Agentmw Mem0Middleware( user_idalice, chat_modelchat_model, embedding_modelembedding_model, modeboth, ) agent_a Agent(..., toolkitToolkit(toolsawait mw.list_tools()), middlewares[mw]) agent_b Agent(..., toolkitToolkit(toolsawait mw.list_tools()), middlewares[mw])这正是 demo 的做法。安全性的依据在于记忆工具在调用时拿到的是实时的AgentState中间件通过state.session_id解析当前活跃 Agent因此单实例跨多 Agent 共享是安全的。若确实需要每个 Agent 独立的 Qdrant 存储则为每个实例传入带不同vector_store.config.path或collection_name的mem0_config。推荐用 Docker 运行 QdrantWindows 上尤其必要本地磁盘 Qdrant 对单进程 demo 够用但真实部署中很脆弱——Windows 上尤其痛苦文件系统锁语义与 Unix 不同独占锁故障更难恢复。任何超出单进程 Linux/macOS 沙箱的场景都建议把 Qdrant 作为服务运行docker run -p 6333:6333 -p 6334:6334 \ -v $(pwd)/qdrant_storage:/qdrant/storage \ qdrant/qdrant然后把 mem0 指向网络地址而非本地路径from mem0.configs.base import MemoryConfig from mem0.vector_stores.configs import VectorStoreConfig mem0_cfg MemoryConfig( vector_storeVectorStoreConfig( providerqdrant, config{ collection_name: mem0, host: localhost, # Docker 容器 port: 6333, embedding_model_dims: 1536, }, ), ) Mem0Middleware( user_idalice, chat_modelchat_model, embedding_modelembedding_model, mem0_configmem0_cfg, )相对磁盘模式的优势无文件锁争用——多个 Python 进程可同时连接状态跨运行存活无需手动清理文件同一套配置可直接迁移到远程 QdrantQdrant Cloud、自建 Kubernetes 部署只需更换host/port/api_key。记忆作用域user_id×agent_idmem0 在add时给每条记忆打上user_id与agent_id标签search时按这些标签做 AND 匹配。中间件通过scope_search_by_agent标志默认True控制 Agent 维度是否参与检索过滤scope_search_by_agentadd打标search过滤效果True默认user_idagent_iduser_idagent_id严格的按 Agent 隔离。同一用户下Agent A 的记忆对 Agent B 不可见。Falseuser_idagent_id不变仅user_id读宽写窄。同一用户的所有 Agent 共享一个记忆池但每条记忆仍记录写入者可见于 mem0 元数据。agent_id默认取agent.name可通过构造参数agent_id...或agent_idlambda agent: ...覆盖_resolve_client与_async_search中search_agent_id的取值逻辑见 _middleware.py。适合放宽scope_search_by_agent的场景一个用户拥有多个专业化 Agent研究 / 编码 / 日程希望彼此受益于对用户的新发现Agent 的name可能随部署变更但希望记忆跨名称变更持久存在。关于 agent-centric 抽取当前不可达mem0 v2 的抽取提示词ADDITIVE_EXTRACTION_PROMPT含一个条件后缀可将框架从以用户为中心User stated X切换为以 Agent 为中心Agent was informed of X / Agent recommended Y。该后缀由is_agent_scoped bool(filters.agent_id) and not filters.user_id门控——即仅当提供agent_id而不提供user_id时生效。由于Mem0Middleware的user_id是必填参数、总是传入因此该 agent-centric 后缀在当前中间件中永远不可达。实践中这通常没有影响——Agent 的人格与配置通常由系统提示词表达而非长期记忆。服务模式集成接入agentscope.app上述 demo 是库模式——自行构造Agent并把Mem0Middleware放进middlewares[...]。对于通过agentscope.appFastAPI 服务层的生产部署user_id已由框架从X-User-IDHTTP 头流入只需通过extra_agent_middlewares工厂挂钩该参数类型定义于 app/_types.pyfrom agentscope.app import create_app from agentscope.middleware import Mem0Middleware from agentscope.middleware._longterm_memory._mem0._agentscope_adapter \ import build_mem0_config from mem0 import AsyncMemory # 在模块级只构建一次 mem0 client —— 本地 OSS Qdrant 对存储目录 # 持独占锁若按请求构建会在并发流量下死锁。 chat_model ... # 共享的 AgentScope ChatModelBase emb_model ... # 共享的 AgentScope EmbeddingModelBase mem0_client AsyncMemory( configbuild_mem0_config( chat_modelchat_model, embedding_modelemb_model, ), ) async def long_term_memory_factory( user_id: str, # ← 来自认证的 X-User-ID 头 agent_id: str, session_id: str, ) - list: return [ Mem0Middleware( user_iduser_id, clientmem0_client, # 跨请求共享 modeboth, ), ] app create_app( ..., extra_agent_middlewareslong_term_memory_factory, )要点工厂签名是async (user_id, agent_id, session_id) - list[MiddlewareBase]每次组装 Agent 时调用一次即每轮聊天 / 每次定时触发。每次返回全新的Mem0Middleware实例但底层共享同一个 mem0 client。user_id是已认证的调用方由agentscope.app通过get_current_user_id注入当前取自X-User-ID头未来鉴权落地后将改为 JWT。直接转发给Mem0Middleware(user_iduser_id, ...)即可无需 resolver 回调。若使用托管 mem0 Platform把AsyncMemory(config...)换成AsyncMemoryClient(api_key...)即可——工厂形态完全一致也没有 Qdrant 锁问题。底层原理AgentScope 作为 mem0 的后端当传入chat_modelembedding_model时中间件内部通过build_mem0_config_agentscope_adapter.py完成四步装配注册 provider在 mem0 的工厂字典LlmFactory.provider_to_class/EmbedderFactory.provider_to_class中以 provider 名agentscope注册AgentScopeLLM/AgentScopeEmbedding绕过白名单校验mem0 的LlmConfig.validate_config/EmbedderConfig.validate_config硬编码了 provider 白名单不含agentscope。适配器动态构造只放行agentscope的LlmConfig/EmbedderConfig子类完成替换——其他 provider 依旧被拒绝单测 test_naive_from_config_path_still_rejected 验证了不经过此助手直接构造MemoryConfig(provideragentscope)会触发 pydanticValidationError构建 AsyncMemory其.llm与.embedding_model全部路由到 AgentScope 适配器同步/异步桥接mem0 的LLMBase.generate_response/EmbeddingBase.embed是同步接口AgentScope 模型是异步的。_AsyncBridge_agentscope_adapter.py在独立守护线程上长期运行一个事件循环通过run_coroutine_threadsafe提交协程并阻塞等待结果——事件循环存活于 bridge 生命周期内因此模型内部的异步客户端如 Ollama 的AsyncClient的连接池可以跨调用复用而不是每调一次就被关闭。AgentScopeLLM.generate_response会把 mem0 的 OpenAI 风格消息字典转换为 AgentScope 的Msg对象system/user/assistant 三种角色未知角色静默丢弃支持流式与非流式模型流式响应被排空、取末块符合 AgentScope 流式契约并把ChatResponse拍平回 mem0 期望的str或含tool_calls的dictToolCallBlock.input是 JSON 字符串会解析回 dict解析失败则保留原文。AgentScopeEmbedding.embed则把文本列表送入 AgentScope embedding 模型并返回第一个向量。上述转换均有对应单测mem0_agentscope_adapter_test.py。维度一致性要求embedding 模型的dimensions必须与向量库期望的维度一致——mem0 默认 Qdrant 期望 1536正好匹配 DashScopetext-embedding-v2的dimensions1536也是 oss_demo.py 中使用的值demo 用的是text-embedding-v4。端到端 demo 解读oss_demo.pyoss_demo.py 是可直接运行的单文件演示要点如下模式切换顶部MODE both可改为static_control或agent_control观察差异干净起点每次运行先删除/tmp/qdrant与~/.mem0/history.db保证可复现刻意不用mem0_client.delete_all()因为 qdrant-client 的本地 SQLite 层在 mem0 并行asyncio.gather删除下存在竞态两个独立的聊天模型实例Agent 与 mem0 各用一个DashScopeChatModelqwen3.7-max。原因是 Agent 在应用事件循环上调用模型而 mem0 适配器在其专属 bridge 事件循环上调用模型异步 HTTP 客户端及其连接池绑定事件循环共享同一实例可能触发 bound to a different event loop显式向量库配置MemoryConfig(vector_storeVectorStoreConfig(providerqdrant, config{collection_name, path/tmp/qdrant, embedding_model_dims1536, on_diskFalse}))——这里只是把 mem0 默认的本地 Qdrant 显式写出便于按注释替换为 Docker/远程 Qdrant两个会话SESSION 1 告诉 Agent 默认暗色 matplotlib 家在杭州SESSION 2 新建空上下文的 Agent 索要柱状图。每轮通过agent.reply_stream事件流打印各中间件贡献行首的static/agent标签标明控制路径[mem0 → context (static)]——ReplyStartEvent时从state.context提取的记忆注记子弹列表[tool call (agent)]—— Agent 自主发起的search_memory/add_memory调用[assistant]—— 由TextBlockDeltaEvent增量拼接的最终回复[context → mem0 (static)]—— 回合结束后静态写回抽取出的新事实用mw._client.get_all(filters{user_id: ...})前后对比得出。托管 Platform 切换只需把Mem0Middleware(...)构造替换为clientAsyncMemoryClient(api_keyos.environ[MEM0_API_KEY])oss_demo.py内# For the hosted mem0 Platform, swap …注释处给出了完整示例其余逻辑完全一致且无需本地 Qdrant / 向量库配置。小结三种构造路径models / models mem0_config / client配合优先级矩阵兼顾库内易用与生产可控三种控制模式覆盖无感知自动记忆static_control、Agent 自主决策agent_control与双轨并行both单个中间件实例跨 Agent 共享安全Qdrant 独占锁问题用 Docker 服务化解决scope_search_by_agent精确控制按 Agent 隔离还是按用户共享服务模式通过extra_agent_middlewares工厂接入user_id由X-User-ID头自动注入底层用AgentScopeLLM/AgentScopeEmbedding 常驻 bridge 事件循环让 mem0 复用 AgentScope 既有模型无需为记忆功能另配模型密钥。需要深入验证行为时可参考 mem0_middleware_test.py构造校验、三模式行为、工具语义、兜底写路径与 mem0_agentscope_adapter_test.py消息转换、响应拍平、provider 注册、config 覆盖两个测试文件中的完整断言。【免费下载链接】agentscopeBuild and run agents you can see, understand and trust.项目地址: https://gitcode.com/GitHub_Trending/ag/agentscope创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考