来源:详细设计.html · 适用版本 memosima ≥ 0.6.6 · 上游 README:../README.md

Prism (Memosima) 详细设计文档

本文档是面向源码贡献者的 全链路设计参考,从架构分层、模块边界、数据库 schema、任务生命周期、提示词契约、向量与提醒子系统,到 REST API 与部署拓扑。目标是让任何一位新加入的工程师,不必反复阅读源码即可定位修改点、评估影响半径

1. 系统目标与设计原则

1.1 业务目标

1.2 设计原则

  1. Sidecar 解耦:所有 AI 能力放在伴生进程,Memos 主站二进制保持纯净,升级不互锁。
  2. API + Worker 双进程:API 层只接收事件并入队,所有耗时处理移至 Worker,避免长轮询拖垮请求线程。
  3. 幂等 + 队列:每个 memo 事件唯一映射一条 jobs.idempotency_key,重投/重试不会重复整理。
  4. 配置三层:环境变量 > config/.env.local(密钥) > config/*.yaml(静态结构),所有改动可热写回。
  5. 降级而非失败:嵌入服务、文档解析、Webhook 不可达时自动降级而非中断整条流水线。
  6. 可观测:所有关键阶段写入 system_logs 与 stdout,管理页面可在线检索。

2. 顶层架构

Caddy Gateway 边缘反代 · 静态文档 · GATEWAY_PORT=8085 → :80 /admin/* /health /webhooks/* /docs/* 其余全部路径 sidecar-api FastAPI · Uvicorn (--factory,开发态加 --reload) Admin UI · Webhook 入口 · QA / RAG 36 REST 路由 · Bearer 鉴权(/admin/ui 例外) memos (主站 :5230) neosmemo/memos:stable memos · comments · relations resources · PAT · webhooks sidecar.db (SQLite · WAL) jobs · memos · tag_candidates artifacts · reminders · vector_units · logs sidecar-worker asyncio 主循环 · poll_interval 2s poll memos · process jobs send reminders · embed chunks 外部服务(按需调用 · 全部支持降级) LLM 推理 DeepSeek · OpenRouter · OpenAI(默认 DeepSeek v4-flash) Embedding SiliconFlow · BAAI/bge-m3 (vector_search.enabled=true) 解析 / 推送 MinerU (Office/PDF) · Bark Webhook (提醒) 读写 claim_next_job Memos REST API · 创建 AI 整理 memo + REFERENCE QA 面板 内部数据/控制 Memos REST 外部 HTTPS(可降级) Sidecar 进程 Memos 主站
图 2-1:Prism 顶层架构 — 双 sidecar 容器共享 SQLite,所有外部依赖均可降级

2.1 进程拓扑

容器进程入口角色
memosima-gatewaycaddy run边缘反代 + 路由分发 + 静态文档
memosima-memosneosmemo/memos:stable原生 Memos 主站
memosima-sidecaruvicorn memosima.api.app:create_app --factoryAPI + Admin UI
memosima-workerpython -m memosima.worker.runner异步整理 / 提醒派发
两个 sidecar 容器共享同一个 data/sidecar/sidecar.db,依赖 SQLite WAL 模式保证并发读写安全;任务通过 claim_next_job()UPDATE ... WHERE status='pending' 原子领取,多个 worker 可水平扩展(默认 1 个)。

3. 源码模块分层

src/memosima/
├── __init__.py              # 包版本:importlib.metadata.version("memosima")
├── api/                     # FastAPI 应用层
│   ├── app.py               # create_app() 工厂,36 路由
│   ├── admin_ui.py          # 管理后台 SPA(HTML/JS 内嵌字符串)
│   ├── security.py          # require_admin Bearer 校验
│   └── webhooks.py          # extract_memo_uid / build_idempotency_key
├── core/                    # 业务无状态算法层
│   ├── config.py            # AppConfig dataclass + load_env_file
│   ├── prompts.py           # 提示词模板加载
│   ├── taxonomy.py          # 标签治理(active/candidate/disabled/alias)
│   ├── attachments.py       # Draw.io / Mind Elixir 本地解析
│   ├── document_parsers.py  # MinerU REST 客户端
│   ├── admin_entry.py       # 管理入口 memo 渲染
│   ├── summary.py           # AI 整理 memo 文本组装
│   └── logging_handler.py   # 自定义 Handler → system_logs 表
├── db/
│   └── store.py             # SQLite Store + dataclass + migrate()
├── llm/
│   └── provider.py          # OpenAICompatibleClient + EmbeddingClient
├── memos/
│   ├── client.py            # Memos REST 封装
│   └── probe.py             # CLI 探针:注册用户 / PAT / Webhook
└── worker/
    └── runner.py            # Worker 主循环 + 整理流水线

3.1 层间依赖规则

3.2 关键数据类(db/store.py

下列字段为 Python dataclass 暴露给上层的属性名(不带 SQL 列的 _json 后缀;JSON 列在 store 内部已统一序列化/反序列化为 dict / list)。

4. 数据库 Schema

数据库文件:data/sidecar/sidecar.db(默认);统一携带 workspace_id,未来可演进多工作区。下面列出的是 SQLite 列名(带 _json 后缀的列由 store 内部统一负责 JSON 序列化);上层调用方应只看 §3.2 中的 dataclass 字段,无需感知列后缀。

-- 工作区(当前固定 default)
workspaces(id PK, name, created_at, updated_at)

-- memo 元数据(既包含原始 memo 也包含 AI 整理 memo)
memos(id PK, workspace_id FK, memos_uid, type, source_memo_uid, content_hash, status,
      created_at, updated_at,
      UNIQUE(workspace_id, memos_uid, type))

-- 任务队列(核心)
jobs(id PK, workspace_id FK, type, status, idempotency_key, payload_json, result_json,
     error, retry_count, created_at, updated_at,
     UNIQUE(workspace_id, idempotency_key))
INDEX idx_jobs_status_created(status, created_at)

-- 候选标签队列
tag_candidates(id PK, workspace_id FK, path, parent_path, status, reason,
               source_memo_uid, similar_tags_json, confidence, reviewer_note,
               created_at, updated_at,
               UNIQUE(workspace_id, path))
INDEX idx_tag_candidates_status_created(status, created_at)

-- 正式业务标签
business_tags(id PK, workspace_id FK, path, status, source, created_at, updated_at,
              UNIQUE(workspace_id, path))
INDEX idx_business_tags_status_path(status, path)

-- 附件解析结果
artifacts(id PK, workspace_id FK, memo_uid, resource_uid, kind, content_markdown,
          metadata_json, created_at,
          UNIQUE(workspace_id, memo_uid, resource_uid, kind))
INDEX idx_artifacts_memo_kind(workspace_id, memo_uid, kind)

-- 提醒事项
reminders(id PK, workspace_id FK, source_memo_uid, title, body, due_at, timezone,
          status, confidence, raw_text, sent_at, error, created_at, updated_at,
          UNIQUE(workspace_id, source_memo_uid, due_at, title))
INDEX idx_reminders_status_due(status, due_at)

-- 向量分块
vector_units(id PK, workspace_id FK, memo_uid, chunk_text, embedding_json, created_at)
INDEX idx_vector_units_memo_uid(workspace_id, memo_uid)

-- 结构化系统日志
system_logs(id PK, workspace_id FK, timestamp, level, component, message)
INDEX idx_system_logs_timestamp(timestamp)

4.1 状态机

实体取值转移触发
jobs.statuspendingrunningsucceeded / failed / waiting_userclaim_next_job() / process() / retry 路由;waiting_user 表示需要人工补充信息后再继续
memos.statuspending / linked / ignored在整理结束后由 worker 写回
tag_candidates.statuspending / approved / rejectedAdmin 审核接口
reminders.statuspending / sent / failed / cancelledWorker _send_due_reminders_once()(注意:cancelled 双 L 拼写)

4.2 内置 PRAGMA

5. 任务生命周期

5.1 Worker 主循环(worker/runner.py: Worker.run_once

# 简化伪代码
async def run_once() -> bool:
    if await self._send_due_reminders_once():    # 1. 优先派发到期提醒
        return True
    job = self.store.claim_next_job()             # 2. 抢占下一个 pending job
    if job:
        await self._process_job(job)
        return True
    if await self._ensure_admin_entry_memo_once():# 3. 维护管理入口 memo
        return True
    return await self._poll_memos_once()          # 4. 主动轮询 memos(poll/both 模式)

外层以 worker.poll_interval_seconds(默认 2s)作为节流;任何一步处理过工作就立即重入下一轮,避免拥塞。

5.2 memo → AI 整理 端到端时序

Memos (主站) REST API · Webhook sidecar-api FastAPI · Webhook 入口 sidecar-worker asyncio · 整理流水线 外部服务 LLM · MinerU · Embed 1 POST /webhooks/memos memo.created / updated 2 build_idempotency_key() store.create_job("organize_memo", ...) → jobs.status = pending 3 store.claim_next_job() 原子 UPDATE WHERE status='pending' 4 GET memo + relations + resources 附件二进制流 5 本地解析 .drawio / Mind Elixir zlib + base64 + XML · DFS → Markdown 6 MinerU /file-urls/batch Office/PDF → Markdown(缺 Key 时跳过) 7 LLM organize_memo() 返回 JSON: title/summary/tags/todos 8 合并标签 · 拼音归一 · 候选入队 summary.build_summary_memo_content() 9 POST /memos + POST /relations (REFERENCE) 原文 ↔ AI 整理 memo 原生双向锚定 10 extract_reminders + embed chunks 仅当含 #提醒 / vector_search.enabled=true 11 jobs.completed · memos.linked 失败 → status=failed, retry_count++ (max 3) 线型: Memos REST 入队 / 自调用 外部 HTTPS(可降级) DB / 内部操作
图 5-1:单条 memo 从写入到 AI 整理完成的完整时序(11 步,所有外部调用支持降级)

5.3 错误与重试

5.4 幂等键

webhooks.build_idempotency_key(memo_uid, event):以 memo_uid + 事件类型派生,确保 Memos 重投同一事件不会重复入队;jobs 表上 UNIQUE(workspace_id, idempotency_key) 提供数据库级守门。

6. LLM Provider 抽象(llm/provider.py

6.1 OpenAICompatibleClient

6.2 Provider 切换

6.3 EmbeddingClient

6.4 检索算法

6.5 QA 双通道

POST /admin/qa/generate-prompt

7. 附件解析流水线(core/attachments.py + core/document_parsers.py

7.1 准入

7.2 解析器路由

后缀走向说明
.drawio本地 zlib + base64 + XML抽取所有 <mxCell value>,过滤 HTML。
.drawio.svg本地从 SVG <diagram> 节点抽嵌入字节与上同。
Mind Elixir JSON本地 DFS 递归转 Markdown 嵌套列表。
.txt / .md / .json(无 schema)直接读取直接当作文本入 artifacts。
.doc(x) / .xls(x) / .ppt(x) / .pdfMinerU /api/v4/file-urls/batch/api/v4/extract-results/batch/周期 poll_interval_seconds,至多 max_polls 次。

7.3 缓存与幂等

8. 标签治理(core/taxonomy.py + config/taxonomy.yaml

8.1 三类标签

8.2 治理规则

  1. 唯一末级:同名末级标签全局唯一,禁止 #项目/数管#其他/数管 并存。
  2. 别名归一aliases[].alias → target 自动映射,提交 AI 之前先归一。
  3. 禁用列表disabled[] 中的标签即便 AI 提出也被丢弃。
  4. 拼音模糊去重:候选标签入队前做拼音首字母比对,去除 #部署 / #deployment / #deploy 类污染。
  5. 限额limits.max_ai_active_tags(默认 5)、limits.max_ai_candidate_tags(默认 2)。

8.3 审核流

9. 提醒子系统

9.1 触发与抽取

  1. 整理流水线检测到正文包含 reminders.trigger_tag(默认 #提醒)。
  2. 调用 llm.extract_reminders():携带 nowtimezone(默认 Asia/Shanghai)。
  3. LLM 返回 items[].due_at(ISO 8601 + 时区偏移),低于 confidence_threshold(默认 0.75)或 needs_clarification=true 会被打回为待澄清。

9.2 派发

9.3 配置热修改

GET/PUT /admin/reminders/config 实时调整触发标签、阈值、Webhook URL;Webhook URL 写入 config/.env.local

10. 备份 / 恢复

10.1 格式

ZIP 包,根目录布局(实际由 _create_backup_archive / _backup_config_files 生成):

manifest.json            { "kind": "memosima-sidecar-backup",
                           "version": 1,                       # 整数版本号,便于校验
                           "created_at": "2026-05-27T...",
                           "workspace_id": "default",
                           "database_path": "data/sidecar/sidecar.db",
                           "config_files": ["config/app.yaml", ...],
                           "restore_behavior": "database_only" }
database/sidecar.db      # 通过 SQLite Online Backup API 拷贝
config/app.yaml          # 备份只包含 4 份 yaml 配置,便于查阅 / 二次手工恢复
config/models.yaml
config/prompts.yaml
config/taxonomy.yaml
⚠ 备份不包含 config/.env.local:所有 API Key、Webhook 密钥等敏感凭据均不会进入 ZIP,避免备份文件泄露密钥;恢复后请在新环境单独配置环境变量或重新通过 Admin UI 写入 Key。

10.2 端点

10.3 backup.sh / restore.sh

11. 管理 REST API 全量索引

所有 /admin/* 接口默认要求 Authorization: Bearer ${SIDECAR_ADMIN_TOKEN},校验逻辑在 api/security.py: require_admin
例外/admin/ui(管理后台 SPA 单页 HTML 本身)以及 /health/webhooks/memos 公开免鉴权——SPA 加载后调用的所有 admin API 仍需在浏览器侧带 Bearer,未鉴权访问 UI 只能看到静态壳。如需更高隔离请在网关层为 /admin/ui 单独加 Basic Auth 或 IP 白名单。
字段细节以 src/memosima/api/app.py 中对应路由的 Pydantic 模型为准。

11.1 健康与入口

方法路径说明
GET/health返回 {status, version, worker_heartbeat_at}
GET/admin/ui内嵌 HTML/JS 单页
POST/webhooks/memosMemos 推送回调,202 Accepted;内部按 memo_uid 入队

11.2 任务队列

方法路径说明
GET/admin/jobs分页 + 过滤 (status/type);status 取值 pending / running / succeeded / failed / waiting_user
POST/admin/jobs/{job_id}/retry强制将单个 failed / waiting_user Job 重置为 pending;可选 body {prompt_override: {system, user}} 覆盖本次 LLM 提示词
POST/admin/jobs/reprocess-memobody 必填 memo_url_or_uid(支持完整 Memos URL 或裸 UID),可选 model_provider / model_name / prompt_override,强制重新整理指定 memo
POST/admin/jobs/batch-reprocess-tagbody 字段 tag(单标签)或 tags(标签数组)+ relationAND/OR,默认 OR),可选 model_provider / model_name / prompt_override;按业务标签批量重排

11.3 提示词

方法路径说明
GET/admin/prompts一次性返回三种模板
PUT/admin/prompts/organize-memo写回 config/prompts.yaml.organize_memo
PUT/admin/prompts/tag-summary写回 config/prompts.yaml.tag_summary
PUT/admin/prompts/reminder-extraction写回 config/prompts.yaml.reminder_extraction

11.4 模型

方法路径说明
GET/admin/models列 provider + default + 已配置的 Key 状态(不回显明文)
PUT/admin/models切换默认 provider/model;可选附带 API Key 写入 .env.local

11.5 标签

方法路径说明
GET/admin/tag-candidates分页候选标签
POST/admin/tag-candidates/{id}/approve升级为正式业务标签
POST/admin/tag-candidates/{id}/reject拒绝并记录原因
GET/admin/tags/business列出全部正式业务标签
POST/admin/tag-summaries触发标签总结 Job;body 字段 tag(单标签)或 tags(数组)+ relationAND/OR,默认 OR),limit 默认 50(1-200);可选 system_prompt_override / user_prompt_override(注意是两个独立字段,不是 prompt_override

11.6 提醒

方法路径说明
GET/admin/reminders列表 + 过滤 (status)
POST/admin/reminders/{id}/retry重发
POST/admin/reminders/{id}/cancel取消 pending
GET / PUT/admin/reminders/config触发标签、阈值、Webhook

11.7 向量与 QA

方法路径说明
GET / PUT/admin/vector-search/configprovider、模型、Key、enabled 开关
POST/admin/qa/generate-promptbody: {question, tags?, logic?, use_vector?} → 返回拼装好的「超级 Prompt」

11.8 文档解析

方法路径说明
GET / PUT/admin/document-parserMinerU Token、model_version、超时与轮询参数

11.9 Memos 同步

方法路径说明
GET / PUT/admin/memos/configbase_url / PAT / 入口模式 / 可见性
POST/admin/memos/delete-all DANGER清空 Memos 主站全部 memo;仅校验 Admin Bearer,无额外确认 header。请在调用前自行二次确认,建议先调用 /admin/backups/download 留底

11.10 备份 / 系统

方法路径说明
GET/admin/backups/download流式 ZIP
POST/admin/backups/restoremultipart 上传 ZIP
POST/admin/database/reset DANGERdrop 所有表并重建(保留配置)
GET/admin/logs系统日志查询(component/level/keyword)
POST/admin/logs/clear清空 system_logs

12. 部署与运维

12.1 一键部署

mkdir prism-prod && cd prism-prod
bash <(curl -s -L https://raw.githubusercontent.com/nabule/Prism/master/deploy.sh)

deploy.sh 行为:

  1. 初始化 config/data/{memos,sidecar}/logs/caddy/
  2. 生成 gateway/Caddyfileconfig/app.yaml(仅当缺失)。
  3. openssl rand -hex 16 生成 SIDECAR_ADMIN_TOKEN 写入 .env
  4. docker compose -f docker-compose.release.yml pull && up -d
  5. 探测公开 host 与 GATEWAY_PORT,把 PRISM_PUBLIC_BASE_URL 写入 .env 并重建 sidecar / sidecar-worker,供 Memos 管理入口 memo 生成正确网关链接。

12.2 镜像

12.3 端口

12.4 多租户隔离

单实例严禁混用多人。共享 sidecar.db 会导致跨账号召回,请按下方方案做物理隔离。

12.5 热开发

13. 测试与质量门

类型命令范围
单元测试npx nx test sidecar(等价 uv run pytesttests/ 覆盖 LLM client、Memos client、attachments、taxonomy、reminders、summary、QA 拼装
语法编译npx nx run sidecar:compileuv run python -m compileall -q src tests
镜像构建npx nx build sidecardocker compose build
Memos 探针npx nx run sidecar:probe-memos初始化用户、签发 PAT、注册 Webhook、验证 sidecar 健康

测试约定

14. 可扩展点与改造指引

需求改造位置注意事项
新增 LLM provider(如 Anthropic、Qwen)config/models.yaml.providers.* + llm/provider.py(若协议非 OpenAI 兼容则需扩展 OpenAICompatibleClient 抽象)timeout 下限 ≥ 90s
替换嵌入服务llm/provider.py: EmbeddingClient + vector_search.* 配置注意向量维度与现有 vector_units 兼容;不同维度需先 POST /admin/database/reset 或新增 dimension 字段
引入 sqlite-vss / 外置向量库db/store.py 中向量检索方法 + 索引建表 SQL保留现有 vector_units 作为冷备
新附件格式core/attachments.py 添加 parser;limits.allowed_parse_extensions 加白名单优先本地解析,避免上传第三方
多工作区已埋点 workspace_id,需在 API 入参增加 workspace 选择 + Admin UI 多实例切换数据隔离已在 schema 层完成
新管理端面板api/admin_ui.py 内嵌字符串(HTML/JS),后续可拆为独立前端项目当前为零依赖单文件 SPA
替换 Bark 为飞书 / 钉钉worker/runner.py: _send_reminder body 适配保持失败降级与 retry 语义

15. 风险与已知约束

16. 后续路线建议(非强制)

  1. 向量检索引擎升级:迁移到 sqlite-vss / Qdrant,移除 Python 端 cosine。
  2. 多模型路由:根据 memo 长度 / 类型自动选 provider(短 → DeepSeek,长 → Gemini Pro)。
  3. 流式输出:QA 接口支持 SSE,前端实时显示「超级 Prompt」生成进度。
  4. 多工作区:上线 workspace_id 路由参数,配套独立 PAT / 标签体系。
  5. 细粒度权限:拆分 Admin Token 为 read-only / write 两级,方便给运维只读访问。

本文件随源码同步更新;如新增 / 修改 API 或配置项,请同步刷新 §11§4