橘子のBlog
首页项目归档照片墙音乐灵境说说杂谈友链关于
封面

嵌入式图存储:打包版免 Docker/Neo4j

写作时间:2026-08-14 20:00:02
# Kmatch

背景与结论

现状:我的Kmatch 的知识库 JSON 真相源(222 节点 + 324 题)已经随安装包分发(electron-builder extraResources 带 data/),但运行时所有图查询/向量检索只走 Neo4j,用户必须自己 docker 起 Neo4j 再手动跑导入脚本。全部 Cypher 收口在 backend/app/graph/engine.py 一个类(932 行 ~30 方法),其余代码只依赖 KnowledgeGraph 方法签名 —— 替换点单一,是理想收口。

方案:新写 EmbeddedKnowledgeGraph(内存图 + SQLite 持久化动态数据 + 分层 embedding 缓存),KMATCH_GRAPH_STORE=embedded|neo4j env 切换;打包版默认 embedded,开发/Docker 链路保留 Neo4j 不动(488 存量测试均 mock driver,不受影响)。

实施步骤

1. embedding 预生成缓存 — backend/scripts/build_embedding_cache.py

  • 复用 import_knowledge_base.py 的 embedding 逻辑,一次性算全部节点向量,输出 data/knowledge_base/embeddings.json(node_id → 1536 维向量,保留 4 位小数,~几 MB),提交仓库随 extraResources 分发。
  • 构建期跑一次即可,装完即有完整离线语义检索。

2. 核心新模块 — backend/app/graph/embedded.py(~650 行,工作量主体)

与 Neo4j 版同方法面的 EmbeddedKnowledgeGraph:

  • 加载:启动时经 kb_store 读 KB_DIR 全部节点/题目 JSON → 内存 nodes dict + REQUIRES 正反向邻接表 + questions_by_node。
  • 图遍历:纯 Python BFS 实现 get_prerequisites / get_dependents / get_reachable(对应 REQUIRES*1..N 变长路径)。
  • 学习路径:assemble_learning_path 的 Cypher 分层 BFS(MATCH path ... min(length(path)))重写为 Python 多目标 BFS;弱项补丁/配额逻辑(_WEAK_PATCH_LIMIT 等)已是纯 Python,原样复用,用小 fixture 与 Neo4j 版对拍。
  • 向量检索:numpy(新增依赖)cosine 打分 top-k;embeddings 三层合并:shipped embeddings.json 为底 + state.db 覆盖层(见下)+ 内存新算。无向量节点跳过语义检索、保留图召回(沿用现有纯图降级语义)。
  • CRUD 同步:upsert/delete_knowledge_node、upsert/delete_question 只更新内存索引 + 触发增量 embedding(JSON 真相源写回已由 api/kb.py 完成,不重复)。
  • 回答「题库会变」:增删改节点时若 embedding client 可用 → 单点补算写入覆盖层;不可用 → 该节点退出语义检索、仍在图检索里。内置 222 节点开箱即有向量。

3. 动态状态持久化 — SQLite(KMATCH_STATE_DIR/state.db)

  • node_status 表:update_node_status / get_node_status(掌握状态,比现在写在 Neo4j 全局覆盖式还更干净)。
  • project_entities / project_relations / project_meta 三表:write/get/delete_project_graph、annotate_risk、link_entity_to_knowledge(场景二项目图谱)。
  • embedding_overlays 表:CRUD 产生的向量变更(node_id, vec blob, text_hash;文本没变沿用缓存)。
  • 数据量小、后端单进程,全内存 + 写穿 SQLite,无并发难题。

4. 工厂 + 启动接线

  • backend/app/config.py:新增 KMATCH_GRAPH_STORE(默认 neo4j)、KMATCH_STATE_DIR、KMATCH_SEED_KB_DIR 三个配置。
  • main.py lifespan:按 store 类型实例化 embedded 或 Neo4j 版(duck-typing 共用方法签名);/api/health 改报 graph_store: "embedded" | "neo4j connected/unavailable"。
  • 可写性:打包版 resources/data 在 Program Files 只读,KB CRUD 写 JSON 会失败。后端启动时若 KMATCH_SEED_KB_DIR 设置且 state 目录无 knowledge_base → 整体拷贝一份(~几 MB)到 <state>/knowledge_base 并把 KB_DIR 指过去;资源目录保持只读。开发模式无该变量,行为不变。

5. Electron 注入 — electron/main/backend-sidecar.js

  • 生产分支 spawn 时注入 KMATCH_GRAPH_STORE=embedded、KMATCH_STATE_DIR=<userData>/state、KMATCH_SEED_KB_DIR=<resourcesPath>/data/knowledge_base;更新头注释(删「Neo4j 仍由用户 Docker 起」)。

6. 依赖与打包

  • backend/requirements.txt 加 numpy>=1.26(PyInstaller hook 自带,spec 预计无需手动 collect,打包时验证)。
  • README/AGENTS.md:安装包章节改为「开箱即用;Neo4j 为开发/服务端可选模式」。

7. 测试与验证

  • 新增 backend/tests/test_embedded_engine.py:加载计数与 import 脚本一致 / BFS 手算小图正确性 / 学习路径与 Neo4j 版对拍 / 假向量(one-hot 1536 维)top-k 断言 / 项目图谱与掌握状态 SQLite roundtrip(tmp_path)/ embedding overlay 增删。
  • 回归:全量 pytest(488 后端测试)。
  • 手工:KMATCH_GRAPH_STORE=embedded 起后端跑学习会话阶段①②、图谱视图、KB 管理增改、场景二项目图谱,再打一次 NSIS 包验证 userData 拷贝与首启流程。

8. 文档

  • devlog 新阶段条目 + AGENTS.md 架构速览/打包节更新 + CONTEXT.md + ADR-0007(双后端存储决策)。

风险点

  • assemble_learning_path Cypher BFS 语义复刻是唯一有对拍风险的点,用小 fixture 双实现对照兜底。
  • embeddings.json 体积与 numpy 进 PyInstaller 产物,打包时实测确认。

‍

avatar

橘子origin

一名正在和算法、数据结构死磕的计算机专业大二大学生! 我相信,代码是逻辑的诗篇,而算法是其中最凝练的修辞。

RECOMMENDED

Origin-Blogs 博客构建介绍(Next.js + python + Vercel部署)

2026-05-31 23:14:39

Hello-Agent:构建一个能处理分步任务的智能旅行助手暨拓展

2026-06-06 09:12:46

龙芯嵌入式 Linux 开发实训第一阶段总结

2026-07-13 15:16:24

Table of Contents