Repository navigation
[Feature] 为 MemOS Dashboard v2 实现后端 #3
Description
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)
- 接口定义:
dashboard-v2分支 根目录的openapi.json - 前端调用与类型:
src/lib/api.ts - mock 实现(形状参照):
server/mock/server.ts - 上游记忆服务:
deployment-v2分支(入口src/memos/api/server_api.py,MCP 网关在deploy/mcp-gateway/) - OpenAPI 规范:OpenAPI Specification 3.1
- 校验工具:Redocly CLI
Activity