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()):
- 创建一个共享 SQLite 连接的
CogDatabase实例 - 在
/api注册 API 蓝图 - 在
/提供前端服务
数据库和配置存储在 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 | — | 按类型过滤(fact 或 perception) |
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 分数合并结果。
上下文字段(params、spatial、robot、task)从上下文 JSON 中自动提取以方便使用。
GET /api/memory/:id
获取单条记忆的完整详情。
响应:完整的记忆对象(memories 表的所有字段,不包含 embedding 二进制数据)。
PUT /api/memory/:id
更新记忆字段。
请求体(JSON):
{
"content": "updated content text",
"confidence": 0.90,
"category": "pattern"
}
允许的字段:content、human_summary、category、confidence、decay_rate、scope、context
响应:
{
"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 秒忙等待超时)。