基于muse pi pro 的多用户RAG智能知识库项目开发
项目的开发目标:为了有效管理,检索,应用手头的各类文档。
一直有意开发一套私有RAG系统,长期开机值守,提供可靠的检索能力,因此,使用手头的muse pi pro 开发一套。
希望可以实现:开发一套完整的检索增强生成(RAG)系统,使用者上传各类文档,系统解析、切分、索引,用户用自然语言提问,大模型仅基于检索到的片段作答,并逐条标注来源(文档名、页码),从机制上杜绝幻觉。系统本地化部署在 K1 开发板上,数据不出内网。
技术上:支持 PDF、Word、Excel、TXT、Markdown、CSV、图片、自动分段切片,表格内容完整提取。语义检索 + BM25 双路召回检索,精准匹配相关片段,支持相似度过滤与 Top-K 控制。回答基于知识库内容,每条答案自动标注来源(文档名、页码),可查可验,杜绝瞎编。知识库可以有效增删改查、元数据筛选、多知识库隔离。不同用户/角色可见不同功能模块和不同知识库,数据安全分权管理。可视化配置管理接入的大模型,支持主要云端模型,也可以使用本地 llama.cpp运行的较小模型等,可随时切换、新增模型,无需改代码。
部署目标:进迭时空 MUSE Pi Pro 开发板(SpacemiT K1,riscv64,8GB 内存,Bianbu OS 2.3.5)
初步的设计规划:
1. 零常驻中间件:检索能力全部沉到 一个 SQLite 文件里。rerank / vlm
2. 单进程、可预期内存:FastAPI 单 worker;重活(解析、OCR)走进程内受限线程池 + 单并发,避免多进程复制内存。
3. 前后端分离但同源托管:前端在 x86 上构建成静态产物,由后端直接托管;板端不装 Node。
4. 能力可降级:向量、重排、OCR、本地大模型全部做成"可选增强",缺依赖时自动降级并在 UI 标注,不阻断主流程。
5. 在线安装为主、离线包兜底:C 扩展依赖一律走 apt(riscv64 有官方包),纯 Python 走 pip;同时产出 wheels/ + debs/ 离线目录,保证 Wi-Fi 断开也能部署。 本地 llama.cpp(可选,RVV)Qwen2.5-1.5B/3B-Instruct GGUF,云端 OpenAI 兼容。
仪表盘观测:内存、检索耗时、模型 token 与费用、失败任务,全部落表 + 仪表盘可视化(对应设计图"仪表盘统计")。
数据库设计:
表 | 作用 | 关键字段 |
department | 部门/组织 | id, name, parent_id, path |
user | 用户 | id, username, password_hash(argon2), dept_id, status, last_login |
role | 角色 | id, name, is_system, desc |
role_permission | 角色→菜单/功能点 | role_id, perm_key |
kb_acl | 角色→知识库(含权重域) | role_id, kb_id, can_read, can_write, domain_weight |
knowledge_base | 知识库 | id, name, desc, owner_dept, visibility, embed_model_id, chunk_size, chunk_overlap |
document | 文档 | id, kb_id, name, ext, size, sha256, pages, status(pending/parsing/ready/failed), error, parsed_at, meta_json |
chunk | 切片 | id, doc_id, kb_id, seq, page_no, text, token_len, is_table, meta_json |
chunk_fts(FTS5) | 全文索引 | rowid→chunk.id, tokens(空格化), text |
chunk_vec(vec0) | 向量 | chunk_id, embedding(float[dim]), model_id |
conversation | 会话 | id, kb_id, user_id, title, last_message_at, state, summary |
message | 消息 | id, conv_id, role, content, model, tokens_in/out, latency_ms, created_at |
citation | 引用 | msg_id, chunk_id, doc_id, page_no, score, snippet |
model_provider | 供应商 | id, name, base_url, api_key_enc, enabled |
model | 模型 | id, provider_id, name, kind(chat/embed/rerank/vlm), ctx, price_in/out, is_default |
parse_task | 解析任务队列 | id, doc_id, status, attempts, worker, started_at, finished_at |
audit_log | 审计 | id, user_id, action, target, ip, detail_json, ts |
stat_daily | 仪表盘聚合 | day, kb_id, docs, chats, messages, tokens |
文档处理模块实现:
格式 | 解析器 | 表格 | 页码 | 备注 |
PDF(文本层) | pdfplumber(版面+表格)+ pypdf(页数/书签) | ✅ extract_tables | ✅ | 扫描页自动识别(无文本层) |
PDF(扫描/拍照) | 页面转图 → 视觉链路(7.4) | 部分 | ✅ | 依赖 OCR/VLM |
DOCX | python-docx(段落 + 表格 + 样式标题) | ✅ | ❌(用段落序号替代) | 保留标题层级用于切片 |
XLSX/XLS | openpyxl(read_only 模式,省内存) | ✅ 整表转 Markdown | 按 sheet | 每 sheet 独立切片,表头行重复注入 |
PPTX | python-pptx(文本框 + 表格 + 备注) | ✅ | 按 slide | — |
TXT / MD | 标准库 / markdown-it-py(保留标题层级) | MD 表格 ✅ | ❌ | 按标题分段优先 |
CSV | 标准库(流式读取,分块) | ✅ | ❌ | 列名作为上下文前缀 |
图片 PNG/JPG | 视觉链路(7.4) | — | — | 缩略图 + OCR 文本 |
其他 | 拒绝并提示支持列表 | — | — | — |
各层实现:
层 | 选型 | 板上实测版本 |
运行时 | Python | 3.12.3(Bianbu 2.3.5 自带) |
Web 框架 | FastAPI + Uvicorn(纯 asyncio,不装 [standard]) | fastapi 0.162.0 / uvicorn 0.54.0 |
数据校验 | Pydantic v2 | 2.14.2 |
数据库 | SQLite(FTS5 全文索引) | 3.45.1 |
中文分词 | jieba | 0.42.1 |
数值计算 | numpy | 1.26.4(apt 安装) |
文档解析 | pypdf / pdfminer.six / python-docx / openpyxl / 原生 zip 读 pptx | 6.21.4 / 20260107 / 1.2.0 / 3.1.5 |
HTTP 客户端 | httpx(访问模型接口) | 0.28.1 |
前端 | 原生 HTML + CSS + JavaScript(无构建、无 node 依赖) | — |
服务管理 | systemd(ragkb.service) | — |
本地推理 | SpacemiT 官方 llama.cpp + Qwen2.5-1.5B Q4_K_M | llama.cpp build 9104 |
本地LLM的实现:
本地 llama.cpp(使用RVV 加速)
本节"编译"方案已按实机情况调整:进迭时空官方提供了带 RVV/IME 优化的预编译包,因此最终采用 SpacemiT 官方 llama.cpp 0.0.9(build 9104)+ 官方 onnxruntime。
下表为最终落地配置与实测值。
项 | 落地配置 / 实测 |
程序 | 官方预编译包 spacemit-llama.cpp.riscv64.0.0.9(build 9104,含 RVV/IME 优化),路径 /opt/llama/current |
依赖 | spacemit-onnxruntime 2.0.3 + spacemit-tcm 3.0.0(0.0.9 必需,否则报 libonnxruntime.so.1 缺失) |
服务 | llama-server --host 0.0.0.0 --port 8080 -c 4096 -t 4 --parallel 1 --batch-size 256 --ubatch-size 256 --jinja --metrics |
内存 | 1.5B Q4_K_M 常驻 835MB(cgroup MemoryCurrent);4B Q4_K_M 需 3476MB |
性能实测 | 1.5B:预填充 11.9 tok/s、生成 3.65 tok/s;0.5B:8.55 / 5.17;4B:4.47(pp64)/ 1.50 |
0.5B 是否更快 | 否。RAG 端到端 1.5B 29.1 s / 0.5B 29.6 s(“出差住宿标准”),43.3 s / 47.0 s(“年假有几天”);0.5B 生成快但预填充慢,长提示下互相抵消 |
关键调参 | 线程固定 4(8 线程时 4B 生成从 1.50 掉到 0.56);--ubatch-size 256 使预填充从 5.57 提升到 11.93 |
模型选择 | 默认 Qwen2.5-1.5B-Instruct Q4_K_M;备选 0.5B(快、质量弱)、Qwen3-4B(质量好、慢、吃 3.4GB) |
启停 | systemd 常驻 llama-server.service(Restart=always、MemoryMax=6G),配套 llama-manage 做启停/换模型/自检;冷启动到就绪 18 秒 |
RAG 端到端 | 5 段引用、每段 1200 字、max_tokens=384 → 70–120 秒/问(瓶颈在预填充);v1.2.1:提示瘦身至每段 800 字、通常 1–2 段 + max_tokens=256 + 回答限 150 字 → 7–10 秒(短答案)/ 19–27 秒(两段引用),抽取式兜底 0.3 秒 |
板端本地模型在本项目中的定位是离线兜底而非主力——它满足"数据不出板",优化后短问答 7–19 秒已可供单人低频使用,但仍无法支撑多人并发,因此模型层必须保持"云端为主、本地可选"的双轨结构。模型可在「模型管理」一键切换默认,也可在问答页按会话临时切换。
最终目录和文件说明:
目录结构
ragkb/
├─ app/ 后端(约 3.2k 行)
│ ├─ main.py 应用入口、静态托管、异常处理
│ ├─ config.py 配置(config.json + RAGKB_* 环境变量)
│ ├─ db.py SQLite 连接、建表、初始数据
│ ├─ schema.sql 表结构(13 张表 + FTS5 虚表)
│ ├─ security.py PBKDF2 口令、HMAC 会话、权限判定
│ ├─ api_auth.py 登录 / 登出 / 当前用户
│ ├─ api_kb.py 知识库、文档、上传、分片
│ ├─ api_chat.py 问答、流式、会话
│ ├─ api_admin.py 部门/用户/角色/模型/统计/任务/审计
│ └─ services/
│ ├─ parsers.py 17 类文档 → 文本段(含页码/章节)
│ ├─ chunker.py 450 字切片、60 字重叠、按章节断点
│ ├─ embedder.py 本地哈希向量(512 维)+ 远程 embedding
│ ├─ retriever.py 二元组+IDF、BM25、向量、加权打分、8 道过滤、命中摘要
│ ├─ llm.py OpenAI 兼容客户端、流式解析、抽取式兜底
│ └─ tasks.py 单工作线程解析队列
├─ web/ 前端(零构建)
│ ├─ index.html
│ └─ assets/{app.js,styles.css,favicon.svg}
├─ scripts/
│ ├─ install.sh 一键安装(建用户、venv、依赖、systemd)
│ ├─ ragkb.service systemd 单元模板
│ ├─ backup.sh 数据库 .backup + 文件打包,保留 14 天
│ ├─ bench-llama.sh 本地模型线程/温度基准测试,输出 Markdown 报告
│ └─ ragkb-backup.{service,timer}
├─ samples/ 演示用三个文件(md / docx / xlsx)
