ESC
深入了解 Web 界面

Web 界面

robotmem 内置了一个 Web 仪表盘,用于浏览、搜索和管理记忆。它作为 Flask 应用程序运行,包含 12 个 REST API 端点。

启动 Web 界面

python -m robotmem web
# 服务器启动于 http://localhost:6889

Web 界面与 MCP 服务器共享同一个 SQLite 数据库。

架构

python -m robotmem web
         │
    ┌────▼────────────────────────┐
    │  Flask App Factory          │
    │  create_app()               │
    │                             │
    │  ┌────────────────────────┐ │
    │  │  api_bp (Blueprint)    │ │
    │  │  /api/*  (12 个路由)   │ │
    │  └────────────────────────┘ │
    │                             │
    │  GET / → index.html         │
    │  CogDatabase (共享连接)     │
    └─────────────────────────────┘

Flask 应用使用应用工厂模式(create_app()):

  1. 创建一个共享 SQLite 连接的 CogDatabase 实例
  2. /api 注册 API 蓝图
  3. / 提供前端服务

数据库和配置存储在 app.config 中:

app.config["ROBOTMEM_DB"] = db
app.config["ROBOTMEM_CONFIG"] = config

REST API 端点

健康检查与统计

GET /api/doctor

健康检查端点 — 验证数据库完整性并报告系统状态。

响应:

{
    "memories": {
        "total": 42,
        "by_type": {"fact": 35, "perception": 7}
    },
    "sessions": {
        "total": 8,
        "by_status": {"active": 1, "ended": 7}
    },
    "fts5": {
        "indexed": 42,
        "expected": 42,
        "ok": true
    },
    "vec0": {
        "indexed": 40,
        "expected": 42,
        "ok": false
    },
    "zero_hit": {
        "count": 5,
        "total": 42,
        "rate": 11.9
    },
    "db_size_bytes": 524288
}
字段 描述
memories.total 活跃记忆数量
fts5.ok FTS5 索引与 memories 表同步
vec0.ok vec0 索引与 memories 表同步
zero_hit.rate 从未被召回的记忆百分比(潜在清理候选)
db_size_bytes 数据库文件在磁盘上的大小

GET /api/stats

聚合统计数据。

响应:

{
    "total": 42,
    "by_type": {"fact": 35, "perception": 7},
    "by_category": {"observation": 20, "pattern": 8, "decision": 5, "constraint": 3},
    "collections": ["default", "grasping", "navigation"],
    "recent_24h": 3
}

记忆操作

GET /api/memories

带过滤器的分页记忆列表。

查询参数:

参数 类型 默认值 描述
page int 0 页码(从 0 开始)
limit int 30 每页项目数(1-100)
collection string 按集合过滤
type string 按类型过滤(factperception
status string active 按状态过滤
category string 按分类过滤(多个用逗号分隔)
confidence_min float 最小置信度阈值
confidence_max float 最大置信度阈值
days int 仅显示最近 N 天的记忆
perception_type string 按感知类型过滤

响应:

{
    "memories": [
        {
            "id": 1,
            "collection": "default",
            "type": "fact",
            "content": "grip_force=12.5N optimal for cylindrical objects",
            "human_summary": "grip_force=12.5N optimal...",
            "perception_type": null,
            "category": "observation",
            "confidence": 0.85,
            "decay_rate": 0.01,
            "source": "tool",
            "scope": "project",
            "status": "active",
            "access_count": 5,
            "created_at": "2026-03-09T10:30:00",
            "updated_at": "2026-03-09T10:30:00"
        }
    ],
    "total": 42,
    "page": 0,
    "limit": 30,
    "pages": 2
}

GET /api/search?q=

使用 FTS5 BM25 排名的全文搜索。

查询参数:

参数 类型 默认值 描述
q string (必需) 搜索查询
collection string 限定集合(空 = 所有集合)
top_k int 10 最大结果数(1-50)

响应:

{
    "results": [
        {
            "id": 1,
            "content": "grip_force=12.5N optimal for cylindrical objects",
            "type": "fact",
            "category": "observation",
            "confidence": 0.85,
            "bm25_score": -3.45,
            "params": {"grip_force": {"value": 12.5, "unit": "N"}},
            "spatial": {"object_position": [1.3, 0.7, 0.42]},
            "created_at": "2026-03-09T10:30:00"
        }
    ],
    "total": 1,
    "query": "grip force"
}

未指定集合时,搜索会跨所有集合运行并按 BM25 分数合并结果。

上下文字段(paramsspatialrobottask)从上下文 JSON 中自动提取以方便使用。

GET /api/memory/:id

获取单条记忆的完整详情。

响应:完整的记忆对象(memories 表的所有字段,不包含 embedding 二进制数据)。

PUT /api/memory/:id

更新记忆字段。

请求体(JSON):

{
    "content": "updated content text",
    "confidence": 0.90,
    "category": "pattern"
}

允许的字段:contenthuman_summarycategoryconfidencedecay_ratescopecontext

响应:

{
    "status": "updated",
    "memory_id": 1,
    "fields": ["content", "confidence"]
}

DELETE /api/memory/:id

软删除记忆(将状态设为 invalidated)。

请求体(JSON):

{
    "reason": "Incorrect sensor reading"
}

如果未提供原因,默认为 "Web UI delete"。

响应:

{
    "status": "deleted",
    "memory_id": 1
}

会话操作

GET /api/sessions

带记忆数量的分页会话列表。

查询参数:

参数 类型 默认值 描述
page int 0 页码
limit int 20 每页项目数(1-50)

响应:

{
    "sessions": [
        {
            "id": 1,
            "external_id": "550e8400-e29b-41d4-a716-446655440000",
            "collection": "default",
            "context": "{\"robot_id\": \"arm-01\"}",
            "session_count": 1,
            "status": "ended",
            "created_at": "2026-03-09T10:00:00",
            "updated_at": "2026-03-09T11:00:00",
            "memory_count": 15
        }
    ],
    "total": 8,
    "page": 0,
    "limit": 20
}

memory_count 字段显示每个会话中有多少活跃记忆(通过 LEFT JOIN)。

GET /api/sessions/:external_id/memories

列出会话内的所有记忆,按时间顺序排列(时间线视图)。

查询参数:

参数 类型 默认值 描述
limit int 20 最大结果数(1-100)

响应:

{
    "memories": [
        {
            "id": 1,
            "type": "fact",
            "content": "Starting calibration...",
            "human_summary": "Starting calibration...",
            "perception_type": null,
            "category": "observation",
            "confidence": 0.85,
            "created_at": "2026-03-09T10:05:00"
        }
    ],
    "total": 15
}

结果按 created_at ASC 排序,用于按时间线查看。

元数据操作

GET /api/collections

列出所有集合及记忆数量。

响应:

{
    "collections": [
        {"name": "default", "count": 30},
        {"name": "grasping", "count": 10},
        {"name": "navigation", "count": 2}
    ]
}

GET /api/categories

列出数据库中所有分类及其数量。

响应:

{
    "categories": [
        {"name": "observation", "count": 20},
        {"name": "pattern", "count": 8},
        {"name": "decision", "count": 5},
        {"name": "constraint", "count": 3}
    ]
}

GET /api/recent-failures

最近的 postmortem 和 gotcha 记忆 — 用于回顾最近学到的教训。

查询参数:

参数 类型 默认值 描述
limit int 5 最大结果数(1-20)

响应:

{
    "failures": [
        {
            "id": 10,
            "collection": "default",
            "type": "fact",
            "content": "Lesson: always calibrate before new session",
            "human_summary": "Always calibrate before new session",
            "perception_type": null,
            "category": "postmortem",
            "confidence": 0.95,
            "created_at": "2026-03-09T10:30:00"
        }
    ],
    "total": 1
}

API 总览

方法 端点 描述
GET /api/doctor 健康检查:FTS5/vec0 同步、零命中率、数据库大小
GET /api/stats 总量计数、类型/分类分布、集合
GET /api/memories 带过滤器的分页列表
GET /api/search?q= FTS5 全文搜索
GET /api/memory/:id 单条记忆详情
PUT /api/memory/:id 更新记忆字段
DELETE /api/memory/:id 软删除记忆
GET /api/sessions 带记忆数量的分页会话列表
GET /api/sessions/:id/memories 会话内记忆(时间线)
GET /api/collections 带数量的集合列表
GET /api/categories 带数量的分类列表
GET /api/recent-failures 最近的 postmortem/gotcha 记忆

三层防御

所有 API 端点遵循相同的防御模式:

层级 阶段 实现
L1 之前 参数验证(类型、范围、非空)
L2 期间 try-except 包装所有数据库操作
L3 之后 统一 JSON 响应 + 错误码

错误响应

所有错误返回包含 error 字段的 JSON:

// 400 请求错误
{"error": "q parameter cannot be empty"}

// 404 未找到
{"error": "Memory #42 does not exist"}

// 500 内部服务器错误
{"error": "Query failed"}

服务器永远不会向客户端返回原始的异常堆栈跟踪。

配置

Web 界面与 MCP 服务器共享配置。关键设置:

设置 默认值 描述
端口 6889 HTTP 服务器端口
数据库 ~/.robotmem/memory.db 与 MCP 服务器相同的数据库
ROBOTMEM_HOME ~/.robotmem 覆盖配置/数据库目录

自定义端口

目前端口在 __main__.py 中硬编码为 6889。要更改端口,请修改 Flask 运行命令。

共享数据库

Web 界面和 MCP 服务器可以同时运行 — SQLite WAL 模式支持并发读取。但同一时间只有一个写入者可以操作(5 秒忙等待超时)。