Skip to content

[Feature] 为 MemOS Dashboard v2 实现后端 #3

Open

Description

@Postroggy

Note

任务目标:为 MemOS Dashboard 实现后端,让页面跑在真实数据上。

下面写的做法只是一种建议,具体怎么设计由你决定;不确定的地方随时沟通。

📌 1. 需求背景与目标 (Motivation & Goal)

MemOS Dashboard 是记忆服务的 Web 控制台。前端已经做完,代码在 dashboard-v2 分支;接口基线在分支根目录的 openapi.json(OpenAPI 3.1,57 条路径,已覆盖 src/lib/api.ts 的全部调用)。

缺的是后端。仓库自带一个 mock(server/mock/),页面靠它跑通。

本任务:用真实数据实现同一套接口。前端代码不改(git diff src/ 为空),后端代码可以改。

页面

13 个路由,截图见 docs/screenshots/:

页面 路由
快速开始 /cn/quickstart
接口密钥 /cn/apikeys
调用日志 /cn/requests
知识库 /cn/knowledgeBase
记忆列表 /cn/memoryList
记忆图谱 /cn/memoryGraph
模型列表 /cn/models
项目 /cn/projects
属性树模板 /cn/profileTemplate
自定义抽取 /cn/customExtraction
用量监控 /cn/usage
接口文档 /cn/docs
关于 /cn/about

顶栏那个「示例数据」标记来自 /api/meta 的 mode:mock 返回 sample,接上真实后端后应是 live。

代码位置

前后端都在本仓库,各占一个分支:

分支 内容
dashboard-v2 前端。根目录就是前端工程:openapi.json、src/、server/mock/ 都在这里。本任务在这个分支上做。
deployment-v2 上游记忆服务。服务本体,加上自部署需要的 compose 与 MCP 网关。

上游

记忆服务是一个自部署的 REST 服务,源码在 deployment-v2 分支(FastAPI,入口 src/memos/api/server_api.py),默认监听 8000。它没有内置鉴权,请求里只有 Content-Type。记忆正文与关系在 Neo4j,向量在 Qdrant。

起一套:deployment-v2 分支的 docker/ 下有 compose,先按 docker/.env.example 配好模型 API Key,docker compose up 会一起拉起记忆服务(8000)、Neo4j(7474 / 7687)和 Qdrant(6333 / 6334)。

调用日志和用量这两个页面的数据源,是同一个仓库 deployment-v2 分支 deploy/mcp-gateway/ 下那个 MCP 网关落盘的审计 JSONL(calls-*.jsonl,每条含 ts / tool / tenant / ms / ok / input / output)。

控制台接口与上游的差异

控制台的接口是按后台管理系统的需要定义的:分页列表、条件筛选、按天聚合、密钥管理、模板增删改。记忆服务提供的 REST 全部面向记忆读写。两者没有现成的对应关系,控制台要的数据得自己组织。

怎么把记忆服务里的数据整理成控制台要的形状,是这个任务的主要难点,方案由你定。下面只是一种做法:

  • 在控制台和记忆服务之间加一层自己的服务(比如放在 server/ 下,和 mock 并列),前端只改 VITE_API_BASE;
  • 也可以改造记忆服务本身,让它直接提供控制台要的接口。

前端已包含的能力

除了基础页面外,dashboard-v2 的前端还包含:

  • 项目页:项目列表 / 创建 / 更新 / 停用 / 归档 / 恢复
  • 项目成员与邀请:成员增删改、邀请创建 / 撤销 / 改角色
  • 账号管理:账号列表 / 禁用 / 启用 / 删除 / 平台管理员标记
  • 知识库:列表 / 详情 / 创建 / 更新 / 删除,以及文件上传 / 下载 / 删除
  • 图谱:/api/dashboard/graph/memories、/api/dashboard/graph/memory
  • 模型清单:/api/dashboard/models
  • 当前账号:/api/auth/me
  • 调用日志:按网关 / 控制台来源筛选

接口基线已补齐。 openapi.json 现在是 57 条路径:45 条 /api/*、9 条 /admin/*,另有 3 条当前前端暂未调用的兼容接口。src/lib/api.ts 里的每条调用都能在 spec 里找到,npx @redocly/cli lint openapi.json 通过。实现时以它为准;如果发现定义与页面实际行为不符,先改 spec 再改代码,两边保持一致。

形状参照 server/mock/ 与页面行为即可。


💡 2. 需求功能 (Expected Features)

(1) 以接口定义为规格

openapi.json 是权威定义,实现逐条对齐。以下几处与常规做法不同:

  • 统一返回格式 { code, data, message },code === 0 表示成功。HTTP 状态码不参与判断,200 + 非 0 code 也算失败。
  • 列表响应的键各接口不同:records / logs / memListItemResponseList / list 四种并存。
  • 分页参数名固定 pageNum / pageSize。
  • 日志详情的 requestBody / responseBody 是 JSON 字符串,不是对象。
  • 密钥列表下发的是完整密钥,没有掩码字段。

(2) 优先级

优先级 模块 说明
P0 记忆列表 / 详情 / 删除 6 个标签页要映射到记忆服务的记忆类型
P0 调用日志列表 / 详情 数据源是审计 JSONL
P0 运行信息 /api/meta 判定页面连的是真后端还是 mock
P1 用量聚合 从同一份 JSONL 按天聚合
P1 项目(工作区) 列表、创建、更新、停用、归档、恢复;记忆类接口都要它的 id
P1 账号 / 成员 / 邀请 /admin/* 与 /api/auth/me;项目页依赖
P2 知识库 列表 / 详情 / 创建 / 更新 / 删除 + 文件
P2 自定义抽取 Prompt —
P2 图谱 / 模型清单 只读
P3 密钥 需要写配置,改完需重启网关才生效
P3 属性树模板 —

(3) 不需要实现的接口

下面这些不纳入验收,接口按统一返回格式返回固定数据即可:

模块 mock 方式
额度 / 订阅 返回固定值,页面能渲染即可
技能 skillMD 返回空字符串
用量里的 token 字段 数据源里没有 token 计数;calls 照实算,totalTokens / requestTokens / responseTokens 填 0

🔍 3. 实现要点 (Implementation Notes)

(1) 响应体积

记忆对象里带向量字段(metadata.embedding,1024 维浮点数组),metadata.sources / history 也可能很大。

前端记忆列表一页 10 条。直接透传上游 POST /product/get_memory 时,响应体 0.5 MB;mock 里的 publicMemory() 用白名单投影,同样 10 条只有 8 KB,可直接沿用。

(2) 字段命名不一致

记忆服务和控制台的字段命名不是一套:id vs memoryId、memory vs memoryContent、metadata.created_at vs createTime。映射集中在一处;散落到各个 handler 里会难以维护。

(3) 时间与状态

  • 时间字段一律叫 createTime,值要能被 dayjs 直接解析。
  • 记忆有状态(activated / archived / deleted),列表和统计通常只算 activated,否则数字会差一个量级。
  • 删除是软删,有对应的恢复语义。

(4) 历史数据

调用日志的 JSONL 跨了几代,早期记录缺字段。字段访问一律给默认值,避免页面崩溃。

(5) 账号与鉴权

  • /api/auth/me 要返回当前登录账号;/admin/* 需要管理员身份。
  • 账号、成员、邀请可以落在你的控制面存储里;上游记忆服务本身不做鉴权。
  • 密钥与上游租户的对应关系由你决定,但要让「新建密钥后能用它调通上游」成立。

🧪 4. 验收标准 (Acceptance Criteria)

分两档:必须达到对应 §2(2) 里 P0 / P1 的模块,是及格线;加分项对应 P2 / P3。

主线是两件事:页面上的数据是不是真的来自记忆服务,以及**src/lib/api.ts 里的每一条调用是不是都按 spec 对上了**。

必须达到

一、数据真的来自上游

  • 顶栏显示「真实数据」(即 /api/meta 的 mode 为 live);停掉 pnpm mock 后刷新,数据仍在
  • 往上游写一条带唯一标识的记忆,控制台能查到它;再用上游 REST 按同一个 id 直接查,两边内容一致
  • 控制台报的条数与上游一致(统计范围要在文档里写明,比如是否含已归档)
  • 在控制台删掉这条记忆后,上游 REST 也查不到
  • 经网关调一次上游,调用日志页出现对应记录,事件类型 / 时间 / 耗时与那份 JSONL 里那一行对得上

二、接口逐条对上

  • src/lib/api.ts 的每条 /api/* 与 /admin/* 调用都按 openapi.json 的定义响应(基线已补齐,实现中若有出入同步更新 spec);npx @redocly/cli lint openapi.json 通过
  • 所有调用按定义响应:统一返回格式、4 种列表响应键、分页参数名、requestBody / responseBody 为 JSON 字符串(§2(3) 里列出的返回固定数据即可,但形状同样要对)
  • 记忆列表、调用日志、用量三个页面的分页、筛选、搜索在真实数据上可用
  • 项目下拉能在真实工作区间切换,切换后记忆列表的内容跟着变
  • 项目页的成员、邀请、账号操作可用

三、性能与健壮性

  • 记忆列表单页响应体积 < 200 KB(看 Network 面板里该请求的大小)
  • 日志列表在 ≥ 1 万行的 calls-*.jsonl 上首屏响应 < 1 秒(按 §1 的字段自己造一份即可)
  • 停掉上游后刷新记忆列表,页面给出提示而不是白屏
  • JSONL 里删掉若干字段后页面仍能渲染(早期记录本就没有 input / output)

四、交付

  • pnpm verify 通过
  • 前端 src/ 无改动

加分项

做到哪算哪,不设上限:

  • 密钥:列表、新建、改名、撤销可用,新建的密钥能调通上游
  • 属性树模板:能列出来并看到详情
  • 自定义抽取 Prompt:能读取、保存,并在服务里生效
  • 知识库:文件上传、下载与删除完整可用

📚 5. 参考资料 (References)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions