主题
第 7 章 接入:MCP、API 与框架适配
2026 年 5 月,一家做法律文书审查的公司把记忆层接进了自己的 Agent。第一版只用了三个工具:写入、检索、删除。跑了两个月,效果停在「能记住客户偏好」——冲突没人消解、摘要没人生成、审计没人看。不是能力不好用,而是它们排在工具清单第二十几位,团队只读了前几个。
另一家公司的做法相反:对着清单写了一份对照表,把每个工具对应到流程里的哪一步,结果发现缺的不是工具,是流程——他们的 Agent 从来没有「会话结束」这个事件,摘要工具因此没有触发点。接入不是接线问题:工具面的宽窄、认证方式、适配深浅,会反过来决定调用方写出什么样的代码。
7.1 MCP 工具面的两种策略
7.1.1 兼容性优先与原生深度
MCP(Model Context Protocol)服务器对外的第一层契约是工具名,它决定客户端能不能直接复用已有提示词与流程。Mnemosyne OS 的 tools/list 返回 55 条,其中 44 个是原生工具,11 个是客户端兼容工具。55 与 44+11 的差不是算错——其中 3 条是保留给旧客户端的废弃别名(memory/export-v1、memory/import-v1、memory/claim),它们与下划线版本指向同一批 handler。别名存在的原因很具体:部分 MCP 客户端会拒绝名字里含 / 的工具。去重后的不同能力是 52 个。
三个数经常被混用:55 是条目数,52 是能力数,3 是别名数。用 52 描述能力、55 描述条目都正确,用 55 描述能力则多算了三个。
7.1.2 两种策略各自的代价
兼容性优先的收益是迁移成本:11 个兼容工具复用常规 agent-memory 工具名(add_memory、search_memories、get_memories、update_memory、delete_memory、list_entities 等),已有的 MCP 客户端把地址指过来,提示词不用改。代价是它们只覆盖最基础的能力——冲突消解、渐进式披露、审计校验、会话摘要都没有对应兼容名,用兼容面等于放弃它们。原生深度的收益是能力完整,代价是调用方要读清单、理解每个工具解决什么问题、重新组织提示词。
| 策略 | 工具数 | 收益 | 代价 |
|---|---|---|---|
| 兼容性优先 | 11 | 提示词零改动,当天可用 | 只有基础能力 |
| 原生深度 | 44 | 冲突消解、披露、审计可用 | 需读清单并重组提示词 |
| 混合 | 55 条(52 能力) | 兼容起步再接原生 | 两套名字并存,需定优先级 |
表 7-1 MCP 工具面的三种路径
混合路径在实际落地时最常见:先用兼容名跑通,再逐个把关键流程换成原生工具。风险是两套名字并存后同一语义有两种调用方式,模型会随机选一个。约定「原生优先、兼容仅作回退」能缓解,但它写在文档里,不在代码里。
7.2 工具分类
44 个原生工具按解决什么问题分成三层,11 个兼容工具单列。
| 分类 | 数量 | 解决什么 | 代表工具 |
|---|---|---|---|
| 检索与治理 | 23 | 存取、压缩、审计、图与时序 | retain recall audit |
| 记忆形成层 | 9 | 会话摘要、冲突、任务事实、经验 | session_summarize conflict_resolve |
| 记忆运行时层 | 12 | 注入、画像、披露、度量 | memory_inject profile_card |
| 客户端兼容 | 11 | 常规 agent-memory 工具名 | add_memory search_memories |
表 7-2 MCP 工具的四类划分
记忆形成层与运行时层都是 8.1.0 新增的,各 9 与 12 个。划分不是按代码模块,而是按「一条记忆在生命周期里的位置」:形成层管「该不该存在、以什么形态存在」,运行时层管「怎么进入模型上下文」。它的实际用途是排查缺口——失败点在「Agent 不知道上次聊到哪」是形成层没接,「知道有这条记忆但塞不进上下文」是运行时层没接。
代价是分层有重叠:要一段可直接注入的上下文,既可以用 recall 自己拼,也可以用 memory_inject 一次拿到,两条路都通,但配额账本、降级行为、返回结构不同。这种重叠在能力扩张时不可避免,它把「选哪个」交给了调用方。
7.3 REST API
不是所有环境都能跑 MCP 客户端。浏览器、低代码平台、只支持 HTTP 的编排工具都需要普通接口,REST 层就是为此存在的:单进程、单端口,控制台与 API 共用同一个监听。认证接受三种头:X-API-Key、Authorization: Bearer <token>、Authorization: Token <token>。三种并存不是设计优雅,而是不同调用方生态各习惯一种,强制统一会让接入方写一层转换。
| 方法 | 路径 | 用途 |
|---|---|---|
GET | /v1/status/ | 存活探测与配置 |
POST | /v3/memories/add/ | 抽取并写入(异步,返回事件号) |
POST | /v3/memories/search/ | 语义检索 |
GET/PUT/DELETE | /v3/memories/{id}/ | 单条读改删 |
GET | /v1/event/{id}/ | 轮询事件 |
POST | /v3/graph/* | 图记忆 |
POST | /v1/capsule/ · /v1/expand/ | 压缩与恢复 |
GET | /v1/integrity/ | 链校验 |
GET/POST/DELETE | /v1/keys/ | key 管理 |
表 7-3 REST 接口的主要路由
写入路由是异步的:POST /v3/memories/add/ 返回事件号而非结果,调用方用 /v1/event/{id}/ 轮询。理由是一次写入可能含模型调用,同步等待会让连接长时间挂着;代价是调用方必须实现轮询,间隔设错会造成两种失败——太密压住服务,太疏让「写完立刻读」读不到。单端口共用的取舍是部署简单,反向代理只配一条规则;代价是控制台静态资源与 API 共享线程与连接池,一次大文件下载会挤占 API 带宽。
7.4 框架适配
框架适配的形态由框架自己的历史协议决定。所有适配器只做两件事:模型调用前检索并注入,回合结束后写入。多做的部分属于框架,不属于记忆层——一个既存记忆又回答问题的组件会拥有自己的模型与对用户数据的判断,那时它已经不是记忆层。
| 框架 | 适配方式 | 注意事项 |
|---|---|---|
| LangChain | BaseMemory 兼容历史类 | 只管历史,不接管链状态 |
| LlamaIndex | ChatStore 与检索器桥接 | 索引与记忆是两套结构 |
| CrewAI | 一个 crew 共享长期记忆 | 作用域按 crew 划分 |
| Dify | HTTP 工具定义走 REST | 需处理异步轮询 |
| n8n | HTTP Request 节点编排 | 作用域键别用临时值 |
| Vercel AI SDK | useChat 经 REST 接记忆 | 前端须走代理,勿暴露 key |
| Ollama | 本地模型加本地记忆 | 两边可离线,适合断网 |
| MCP | stdio 与 Streamable HTTP | 两种传输鉴权不同 |
表 7-4 框架适配与注意事项
适配深度差别很大。LangChain 与 LlamaIndex 是代码级桥接,能拿到框架内部的回合边界,「什么时候写」有明确触发点;Dify 与 n8n 是 HTTP 级适配,只能靠编排节点决定时机,「会话结束」这个事件要自己造;Vercel AI SDK 是前端场景,写入必须经服务端代理,否则 key 会进浏览器。代码级桥接因此能用上会话摘要,HTTP 级适配通常只能用基础写入与检索——这不是记忆层的限制,是接入点信息量的限制。
7.5 组件生态与降级报告
组件适配器共 72 个:20 个 LLM、13 个嵌入器、28 个向量库、6 个图存储、5 个重排器。传输分三档:stdlib HTTP 用标准库 urllib 直连,不需第三方包;sdk 档延迟导入厂商 SDK,缺包时明确报出缺哪一个;builtin 档是内置实现,嵌入器 128 维、确定性、离线,另有 hashing 嵌入器支持任意维度。
三档的意义是把「要不要装依赖」的决定权交给部署方:只想跑起来的用户装完主包就能用,要接 Qdrant 的用户才需额外依赖。代价是同一功能在不同档下行为不一致——内置嵌入器的召回质量明显低于真实嵌入模型,而接口签名相同,容易让人以为换 provider 只是换后端。
降级是这套设计里最容易做错的地方。配置的组件构建不出来时(缺 key、端点不可达、SDK 未装),系统回退到内置等价实现,并明确报告回退了什么:
python
m = Memory.from_config({"llm": {"provider": "openai"}})
print(m.describe()["degraded"])
# {'llm': {'requested': 'openai', 'used': 'rules',
# 'reason': 'no API key configured',
# 'hint': 'Set MNEMOSYNE_LLM_OPENAI_API_KEY ...'}}返回里四个字段缺一不可:请求了什么、实际用了什么、为什么、怎么修。只有 used 没有 reason,调用方知道降级了但不知道为什么;没有 hint,知道原因但不知道下一步。静默降级正是要防的失败——它会让配置错误的部署以「能用但效果差」的形态长期运行而无人知道该修哪里。需要硬失败时可打开严格模式:开发与测试期硬失败,生产期软降级。
7.6 切换成本的真实估计
7.6.1 从 Mem0 切换
2026 年的 Mem0 算法同样是单遍 ADD-only 提取,所以「写入模型不同导致迁移困难」这条理由已不成立。真正的差异有四处。第一是工具名:11 个兼容工具直接复用常规名,MCP 客户端改地址即可;Memory 类也接受常规调用形状——同样的类名、方法名、参数名与默认值,代码层面往往只改一行 import。
第二是 update_memory 的语义,这是需要重新设计的地方。只增不改的模型下,更新不是原地改写正文,而是写入新条目并把旧条目置为已被取代。依赖「更新后旧值消失」的代码需要重组——旧值仍在库里,只是默认不返回。这个差异在功能测试里看不出来,在审计或历史查询里才暴露。
第三是图记忆的归属。Mnemosyne 的图内建在同一个 SQLite 文件里,实体链接与多跳遍历零依赖;Mem0 的图记忆(entity linking)是平台能力,开源版官方标注为已移除、无可查询的图与 relations 字段——所以「从开源版迁过来想要图」是新增能力而非平移能力。第四是审计:哈希链账本与 verify_integrity() 能指出具体哪一条被改,Mem0 侧没有对应物,迁移时常被当成附赠,直到第一次需要举证才发现是刚需。
7.6.2 从 Zep 切换
Zep 的 Context MCP Server 暴露 12 个工具:用户自有图 6 个(search_graph、get_user_summary、get_subgraph、get_node_neighbors、list_episodes、add_memory),共享 Context Graph 6 个(list_graphs、search_graph_in、get_subgraph_in、get_node_neighbors_in、list_episodes_in、add_memory_to_graph),另暴露 zep://graphs/directory 资源。它的 search_graph 有 scope 参数,可取 auto / observations / thread_summaries / episodes / nodes / edges——把「要结论还是要原始片段」做成了显式参数。
迁移的难点在返回形态。Zep 的 search_graph 返回已组装好的上下文块,下游直接拼进提示词;Mnemosyne 返回结构化记忆条目,需调用方自己组装。所以从 Zep 迁过来时下游要加一层组装逻辑——这不是功能缺失,是把「怎么组装」的决定权从服务端移到了客户端,而客户端原本没有这段代码。
7.6.3 从 Letta 切换
Letta 的记忆在 agent runtime 内部(当前概念面是 Stateful agents、Conversations、MemFS、shared memory),形态是「有状态 Agent + 它的记忆」;Mnemosyne 是外部服务,Agent 通过接口访问。这是三种迁移里幅度最大的,因为它改的不是接口调用,是架构位置。原本「记忆随 Agent 实例存在」的假设失效后要处理三件事:记忆的生命周期不再由 Agent 控制;多实例共享同一份记忆时作用域要重划;「启动时加载记忆」从隐式变成显式。代价是失去 runtime 内部访问的便利,只能通过接口交换;收益是可替换性:runtime 换掉,记忆还在。
7.7 本章小结
本章确立了三条判断。
第一,工具面的宽窄是一个设计决策,不是能力清单的长度。 11 个兼容工具让迁移当天可用,但用兼容面等于放弃冲突消解、渐进披露与审计;44 个原生工具能力完整,但要求调用方读清单并重组提示词。混合路径最常见,风险是两套名字并存导致模型随机选一个。成立条件是调用方知道自己缺什么;不知道缺什么的团队,工具面再宽也只会用前三个。
第二,适配深度由接入点信息量决定,不由适配器数量决定。 代码级桥接能拿到回合边界,因此能用会话摘要;HTTP 级适配不知道回合何时结束,只能用基础写入与检索。这解释了为什么「支持八个框架」不等于「八个框架上能力相同」。它在框架本身提供回合事件时失效——那时 HTTP 级适配也能拿到边界,深度差异会缩小。
第三,降级必须被报告,静默降级比不降级更糟。 组件构建失败时回退到内置实现,同时给出请求值、实际值、原因与修复提示。静默降级会让配置错误的部署以「能用但效果差」的形态长期运行。成立条件是调用方会读 describe();不读的部署等于自愿放弃这条信息。开发期应反过来——用严格模式让配置问题立刻失败。
三种迁移的成本排序是 Letta 最大(架构位置改变)、Zep 次之(下游要加组装层)、Mem0 最小(名字与调用形状都能对上)。但这个排序只描述代码改动量,不描述风险:Mem0 改动最小,风险却在 update_memory 的语义差异上——它不会让测试失败,只会让历史查询给出不同答案。第 8 章转向另一个维度:以上接入方式都假设有一个能跑进程的地方。当环境不允许常驻进程、不允许本地磁盘、或要求多副本时,这些假设要重新检查一遍。