目录

MCP v2 Memory Engine

一个基于 MCP v2 (streamable-http) 的「时空螺旋记忆引擎」演示工程。

它把「记忆」建模成会随时间演化(形成 → 巩固 → 稳定 → 衰减 → 重巩固)的粒子, 在三维螺旋轨道上运动;后端负责记忆的存储 / 召回 / 演化,前端负责把这一切实时可视化。

技术栈:Python 3.13 + MCP SDK (后端) · React 19 + TypeScript + Vite + Canvas (前端) · BGE-M3 (本地向量召回) · DeepSeek API + MLX (Qwen3-4B / Qwen3-VL-4B) (端云协同推理)。


界面预览

看板总览

图 1 · 看板总览:左侧为 MCP Tool 模拟器(可直接对 memory 工具发起 store / recall / consolidate / decay / evolve 等 action)与三种记忆类型的数量统计; 中间为螺旋 Canvas —— 彩色粒子对应各轨道上的记忆节点,节点间连线表示语义关联,左下角给出各类型计数、平均强度与「时空速率」倍速滑杆; 右侧是实时「记忆流」(Memory Stream)与 MCP 调用日志;顶栏显示端侧 / 云端模型、DS API 与 MCP Server 连接状态、实时帧率。

记忆详情浮层

图 2 · 记忆详情浮层:点击螺旋上的节点后弹出,展示记忆内容、基础属性(强度 / 访问次数 / 创建于 / 当前阶段)、 「时空螺旋演进轨迹」(如 t+0.0s → formation → 强度 30%),以及底部的 mcp.call("memory", {"action": "detail", "id": 15}) 协议映射,便于把界面现象与 MCP 调用一一对照。


1. 整体架构

┌──────────────────────────────────────────────────────────────────────────┐
│                          web/  React 可视化看板                            │
│  TopBar · LeftPanel(Tool 模拟器) · CenterPanel(螺旋 Canvas) · RightPanel   │
│                                                                          │
│  api/mcp.ts  ─ JSON-RPC over HTTP ─┐        store/ClientStore (外部 store) │
│  api/stream.ts ─ subscriptions/listen (SSE) ┘                            │
│  ⚠ 无 Mock 分支,前端始终直连真实 MCP 后端                                  │
└────────────────────────────────┬─────────────────────────────────────────┘
                                 │  POST /mcp  (vite proxy → 127.0.0.1:8000)
┌────────────────────────────────▼─────────────────────────────────────────┐
│                    server.py · MCPServer("Memory Engine")                 │
│                                                                          │
│  tools:      memory(action=...)  ·  read_pic(uri)                         │
│  resources:  memory://snapshot   ·  memory://agent-log                    │
│  notif:      notifications/resources/updated  (500ms tick 后推送)          │
│                     │  action 分发(memory/actions.py)                    │
│  ┌────────────────┐ ▼  ┌──────────────────┐  ┌────────────────────────┐   │
│  │ dashboard/     │    │ memory/          │  │ llm/                   │   │
│  │ Scheduler      │──▶ │ actions.dispatch │  │ DeepSeekClient         │   │
│  │ (500ms tick)   │    │        ↓         │  │ MLXBrain / MLXVLM      │   │
│  │                │    │ MemoryEngine     │◀──│                       │   │
│  └────────────────┘    └──────────────────┘  └────────────────────────┘   │
│      snapshot.py · seed.py · embedder.py · distiller.py                   │
└────────────────────────────────┬─────────────────────────────────────────┘
                                 │
              ┌──────────────────┴──────────────────┐
              │  scripts/agent_loop.py (端侧 Agent)  │
              │  MLX 归一化 → DeepSeek tool-calling  │
              └─────────────────────────────────────┘

数据流(一次记忆写入)

  1. scripts/agent_loop.py 收到生活碎片(文字 / 图片)。
  2. 文字 → MLXBrain(Qwen3-4B)归一化成通顺中文;图片 → MLXVLM(Qwen3-VL-4B)初步描述,并复制进 blobs/ 得到 memory://blob/xxx URI。
  3. DeepSeek 拿到文本 / 图片 URI,通过 MCP tool 调 read_pic(uri) 看原图,再调 memory(action="store") 落库。
  4. server.py 的 memory 工具不做业务判断,直接把参数交给 memory/actions.py::dispatch() 路由到对应 handler。
  5. handler 调 MemoryEngine.store() 写入节点、按类型自动连线、生成粒子,并即时计算 BGE-M3 向量。
  6. Scheduler 每 500ms 推进一次引擎状态并发布 ResourceUpdated 通知;前端收到通知后回拉 memory://snapshot 刷新螺旋画面。
  7. 后台每 8s 触发 evolve()(螺旋演化 / 情景→语义抽象),每 20s 触发 maybe_distill()(DeepSeek 把积累的 episodic 凝练成 semantic / procedural)。

2. 目录结构

mcp_demo/
├── server.py                 # MCP 服务入口:工具/资源声明、订阅总线、lifespan(纯协议层)
├── load_model.py             # MLX 文本模型最小验证脚本
├── load_vl_model.py          # MLX 视觉模型最小验证脚本
├── pyproject.toml            # Python 依赖与工程元数据 (uv 管理)
├── uv.lock / .python-version # 锁定依赖与 Python 版本 (3.13)
│
├── memory/                   # 记忆领域层(与 MCP 协议解耦)
│   ├── engine.py             # MemoryEngine:节点/连线/粒子 + store/recall/consolidate/decay/evolve/distill
│   ├── actions.py            # action 注册表 + dispatch():store/recall/consolidate/... 各一个 handler
│   ├── snapshot.py           # build_snapshot():内部状态 → 前端可消费的快照
│   ├── seed.py               # load_seed_nodes():从 JSONL 灌入预置记忆(MOCK_SEED=true 时)
│   ├── embedder.py           # Embedder:BGE-M3 本地向量化(L2 归一化)
│   ├── distiller.py          # DeepSeekDistiller:episodic → semantic/procedural 凝练
│   ├── models.py             # Pydantic 模型:MemoryRecord / LinkRecord
│   └── store.py              # SQLite + FTS5(含 CJK bigram 查询改写)持久化层
│
├── dashboard/
│   └── scheduler.py          # 后端 tick 循环:驱动引擎 + 发布资源更新通知
│
├── llm/                      # 模型层
│   ├── deepseek_client.py    # DeepSeek API 客户端(OpenAI 兼容)
│   ├── mlx_brain.py          # 端侧 Qwen3-4B:碎片 → 通顺中文
│   ├── mlx_vlm_brain.py      # 端侧 Qwen3-VL-4B:图片 → 初步描述
│   └── tools.py              # memory 工具的 OpenAI function-calling schema
│
├── scripts/                  # 端到端验证 / 演示脚本
│   ├── agent_loop.py         # 完整链路:碎片/图片 → MLX → DeepSeek → MCP tools
│   ├── check_c2.py           # 验收①:DeepSeek 能否按 schema 产出 tool_calls
│   └── check_read_pic.py     # 验收②:read_pic + DeepSeek 多模态链路
│
├── data/
│   └── mock_seed.jsonl       # 30 条预置记忆(跑步主题,episodic/semantic/procedural 混合)
│
├── assets/                   # README 截图:web_1.png(看板总览)/ web_2.png(记忆详情浮层)
│
├── web/                      # 前端可视化看板 (React 19 + Vite)
│   ├── src/
│   │   ├── api/              # mcp.ts (JSON-RPC) / stream.ts (SSE) / sseParser.ts
│   │   ├── store/            # ClientStore (useSyncExternalStore 外部 store)
│   │   ├── hooks/            # 流连接、Agent 日志轮询、tool 调用、速率控制
│   │   ├── renderer/         # SpiralRenderer (Canvas 螺旋渲染) + layout 坐标映射
│   │   ├── components/       # TopBar / LeftPanel / CenterPanel / RightPanel / DetailOverlay
│   │   ├── constants/        # 记忆类型、阶段、示例内容元数据
│   │   └── types/            # Snapshot / MemoryNodeDTO 等 DTO 类型
│   └── vite.config.ts        # 端口 5173,/mcp 反向代理到 127.0.0.1:8000
│
├── bge-m3/                   # 本地嵌入模型 (BAAI/bge-m3, 1024 维, 8192 tokens)
├── blobs/                    # MCP blob 资源目录(图片附件,URI: memory://blob/xxx,运行时自动创建)
└── logs/
    └── agent_calls.jsonl     # agent_loop 写入的 tool 调用日志(前端轮询展示,运行时自动创建)

3. 核心概念

3.1 三种记忆类型

类型 中文 含义 颜色 衰减率 巩固加成
episodic 情景记忆 具体事件与经历(带时空标记) #00f0ff 青 0.85 +0.20
semantic 语义记忆 抽象知识与规律(去时空上下文) #b14aed 紫 0.97 +0.10
procedural 程序记忆 技能与流程(可自动执行) #ffc857 金 0.995 +0.05

衰减率越高越持久:情景记忆最易遗忘,程序记忆最稳定。

3.2 记忆生命周期(Phase)

formation(形成)→ consolidation(巩固)→ stabilization(稳定)→ decay(衰减)→ reconsolidation(重巩固)

  • 被 recall() 命中的记忆会获得强度加成,若处于 decay/stabilization 则回到 reconsolidation(记忆提取后重新变得不稳定,可被新信息改写)。
  • consolidating / decaying 是短暂的视觉态标志,由引擎在 update(dt) 中基于时间戳自动复位(服务端不再依赖 setTimeout)。

3.3 时空螺旋

每个节点拥有 spiralAngle / spiralTargetRadius / spiralTargetY,前端据此建立极坐标映射(renderer/layout.ts)。 三个记忆类型各占一条螺旋轨道;evolve() 会让角度与半径向前推进,episodic 在「强度 > 0.7 且访问次数 > 3」时自动抽象为 semantic。

3.4 时间语义:monotonic 与 wall clock

引擎内部统一使用 time.monotonic() 防系统时钟跳变;对外通过 engine.to_wall_ms() 转成 Unix epoch 毫秒供前端 Date 使用。 occurredAt(事件实际发生时间)与 createdAt(写入时间)分离,支持把「回忆过去」正确锚定到时间轴。


4. 环境准备

前置:Python 3.13、uv、Node.js **20+**。

# 1) Python 依赖
#    必须带 --extra embed:不带的话 uv sync 会把 embed extra 里的 mlx-lm / numpy / transformers
#    以及下方手工安装的包识别为"多余包"并卸载,导致 server.py 无法导入。
uv sync --extra embed

# 2) 运行期还需要的包(目前未声明在 pyproject.toml,需手工安装)
uv pip install openai sentence-transformers mlx-vlm

# 3) 前端依赖
cd web && npm install

依赖对应关系:memory/embedder.py → sentence-transformers + numpy;memory/models.py → pydantic; llm/deepseek_client.py → openai;端侧模型 → mlx-lm / mlx-vlm。

运行期降级只覆盖「模块可导入但初始化失败」:BGE-M3 模型加载失败时 recall 退化为子串匹配, DeepSeek 客户端初始化失败时 distill 关闭——服务仍能启动。若包本身缺失(ModuleNotFoundError), 由于 server.py 在模块顶层 import 这些依赖,进程会直接启动失败。详见第 9 节。

环境变量

变量 必填 说明
DEEPSEEK_API_KEY 调用 LLM 时必填 DeepSeek API Key,deepseek_client.py 从环境变量读取,不硬编码
MOCK_SEED 否 设为 true 时,后端启动后从 data/mock_seed.jsonl 灌入 30 条预置记忆;不设则不灌种子
export DEEPSEEK_API_KEY=sk-xxxxxxxx
export MOCK_SEED=true        # 可选:启动时加载演示种子数据

5. 运行

5.1 启动后端 MCP 服务(端口 8000)

uv run server.py                      # 空引擎启动
MOCK_SEED=true uv run server.py       # 启动即灌入 data/mock_seed.jsonl 的 30 条演示记忆
# → streamable-http @ http://127.0.0.1:8000/mcp
#   stateless_http=True, json_response=True

启动时 lifespan 依次:创建 MemoryEngine → 启动 Scheduler → 加载 BGE-M3 →(若 MOCK_SEED=true)灌入种子数据 → 挂载 DeepSeek 凝练器。 其中 BGE-M3 与凝练器加载失败会自动降级,不阻断服务启动。

5.2 启动前端看板(端口 5173)

前端必须先启动后端:web/src/mock/ 已移除,前端没有任何 Mock 分支, api/client.ts 与 api/stream.ts 始终向 /mcp 发起真实 JSON-RPC 请求。

# 终端 1:后端
uv run server.py

# 终端 2:前端
cd web
npm run dev
# → http://localhost:5173    (vite 将 /mcp 代理到 127.0.0.1:8000)

若后端未启动,看板会保持空状态并在控制台报 MCP request failed / stream error。 启动成功后的界面即上方「界面预览」中的图 1;点击任一节点可看到图 2 的详情浮层。

5.3 运行端到端 Agent(可选)

需先启动后端,并配置 DEEPSEEK_API_KEY:

uv run scripts/agent_loop.py          # 完整链路:文本/图片碎片 → 存储
uv run scripts/check_c2.py            # 仅验证 DeepSeek tool-calling schema
uv run scripts/check_read_pic.py      # 验证 read_pic + 多模态链路 (需 blobs/person.png)

agent_loop.py 顶部的 INPUTS 列表可增删待处理碎片;取消注释 {"kind": "image", "path": "..."} 即可测试图片路径。


6. MCP 接口

6.1 Tools

memory(action, ...) — 统一记忆操作入口

action 关键参数 说明
store content, memory_type(必填), occurred_at, entities, attachment_uri 写入一条记忆,返回新节点
recall query 语义召回(有 Embedder 时用向量,否则子串匹配),命中后提升强度
consolidate — 全局巩固:强度 < 0.9 的节点 +巩固加成
decay — 全局衰减:非 formation 且非巩固中的节点按类型衰减率相乘
evolve — 螺旋演化 + 情景记忆抽象为语义记忆
detail id 单条记忆详情(含 evolution 演化轨迹)
set_speed speed 调整引擎速率(前端时间轴倍速)

所有 action 由 memory/actions.py 的 _ACTIONS 注册表分发,server.py 中的 memory() 只做参数收集,不再含 if/elif 分支。

两点返回契约:

  • 除 detail 外,其余 action 统一返回 {ok, log:{time,method,status,detail}, affected:[id...]},store 额外带 affected_node。
  • detail 直接返回裸 node dict(含 evolution 数组,时间为 wall clock 毫秒),不带 ok/log 外壳 —— 与前端 fetchMemory() 的类型契约一致。

read_pic(uri) — 读取 blobs/ 下的图片,返回 {ok, uri, mime, size, base64}。 URI 必须形如 memory://blob/<name>,且 <name> 不允许包含 / 或 ..(防目录穿越)。

6.2 Resources

URI 说明
memory://snapshot 完整记忆快照(nodes / connections / stats),前端渲染数据源
memory://agent-log logs/agent_calls.jsonl 最近 50 条 tool 调用日志

6.3 通知与订阅

快照不走 SSE 直接推数据,而是 「通知 + 拉取」:

Scheduler 每 500ms ──▶ bus.publish(ResourceUpdated("memory://snapshot"))
                              │
前端 subscriptions/listen (filter: resourceSubscriptions=[memory://snapshot])
                              │  收到 notifications/resources/updated
                              ▼
                     resources/read("memory://snapshot") → 渲染

前端按 MCP v2 modern 路由规范发送 MCP-Protocol-Version / mcp-method / mcp-name 头。

6.4 预置种子数据

data/mock_seed.jsonl 是每行一个 JSON 对象的种子文件,由 memory/seed.py::load_seed_nodes() 读取并逐条走 engine.store():

{"type": "episodic", "content": "今早绕湖跑了十公里,配速五分半", "entities": ["跑步", "湖", "十公里"]}
{"type": "semantic", "content": "用户习惯早上六点起床跑步", "entities": ["跑步", "早起"]}
  • 仅在 MOCK_SEED=true 时加载;不设置该变量则跳过(默认不灌数据)。
  • 字段与 MemoryEngine.store() 对齐:type(必填) / content(必填) / entities / occurredAt / attachmentUri。
  • 容错:非法 JSON、未知 type、空 content 的行会被跳过并记 warning,不影响其余行。
  • 当前 30 条内容为「跑步」主题,episodic 足够多时可被 20s 蒸馏任务自动凝练出新的 semantic / procedural。

7. 关键设计说明

  • 协议与领域解耦:server.py 只负责 MCP 暴露与参数收集,action 的执行逻辑集中在 memory/actions.py 的注册表中, 新增 action 只需加一个 handler 并注册,不必改动工具签名(SDK 依赖签名生成 JSON schema,因此签名保持稳定)。
  • 单一数据源:前端不存在 Mock 实现,所有节点/日志都来自后端 memory://snapshot 与 memory://agent-log,避免双份状态漂移。
  • 时钟安全:引擎内部 monotonic,快照出口转 wall clock,避免系统时间调整导致演化错乱。
  • 降级优先:Embedder / Distiller / 端侧模型任一加载失败都不会阻断服务启动。
  • CJK 检索:SQLite unicode61 会把整段中文当作一个 token,memory/store.py 在查询侧改写为连续 bigram 短语以支持中文子串检索。
  • 多模态折中:端侧 VLM 只做「初步观察」,DeepSeek 通过 read_pic 看原图后做最终判断;base64 不塞进 tool 消息,而是以 image_url 追加 user 消息(对齐官方 agent 做法)。

8. 模块速查表

路径 职责
server.py MCP 服务:memory / read_pic 工具声明、两个资源、订阅总线、lifespan 装配
dashboard/scheduler.py 500ms tick、8s 自动 evolve、20s 自动 distill、资源更新通知
memory/engine.py 记忆节点 CRUD、召回、巩固/衰减/演化/凝练、粒子与连线
memory/actions.py action 注册表与 dispatch(),含 recall 等 7 个 handler 与统一响应壳
memory/seed.py 从 data/mock_seed.jsonl 灌入预置记忆(MOCK_SEED=true 触发)
memory/snapshot.py 引擎状态 → 前端快照(含统计与 phase 标签)
memory/embedder.py BGE-M3 向量化(归一化点积 = 余弦相似度)
memory/distiller.py DeepSeek 凝练:多条 episodic → semantic / procedural
memory/store.py SQLite + FTS5 持久化(CJK bigram 查询改写、触发器同步索引)
memory/models.py MemoryRecord / LinkRecord Pydantic 模型
llm/deepseek_client.py DeepSeek OpenAI 兼容客户端(含 tool calling 透传)
llm/mlx_brain.py Qwen3-4B 文本归一化
llm/mlx_vlm_brain.py Qwen3-VL-4B 图片描述
llm/tools.py memory 工具的 function-calling schema(与 server.py 签名同步)
scripts/agent_loop.py 端侧 Agent 主循环(MLX → DeepSeek → MCP tools)
web/src/api/ MCP JSON-RPC 客户端、SSE 流与解析(无 Mock 分支)
web/src/store/ ClientStore(快照 / 视觉插值 / 日志)+ useSyncExternalStore 绑定
web/src/renderer/ Canvas 螺旋渲染与极坐标布局
web/src/components/ 四大面板与详情浮层
data/mock_seed.jsonl 30 条预置演示记忆(JSONL,可由 MOCK_SEED=true 加载)
assets/web_*.png README「界面预览」截图(看板总览 / 记忆详情浮层)
关于

spiral_memory一个以记忆工具为探针研究MCP Python SDK v2源码的端侧 Agent 项目。以运动场景驱动,基于端侧推理(MLX+Qwen3)+云端大脑(DeepSeek)推理的多模态融合Agent机制的记忆机制:MLX 小脑将模糊片段展开为提示,DeepSeek 大脑凝练出情景/语义/程序三类记忆。前端以时空螺旋实时可视化记忆的形成、凝练与衰减——记忆不会消失,只会变暗。

3.1 MB
邀请码
    Gitlink(确实开源)
  • 加入我们
  • 官网邮箱:gitlink@ccf.org.cn
  • QQ群
  • QQ群
  • 公众号
  • 公众号

版权所有:中国计算机学会技术支持:开源发展技术委员会
京ICP备13000930号-9 京公网安备 11010802047560号