docs: 新增 README.md 和 CLAUDE.md 项目文档
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
16a19a12f5
commit
bd9f03da65
231
CLAUDE.md
Normal file
231
CLAUDE.md
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
# CLAUDE.md — elec_rag_ui 项目约定与开发指南
|
||||||
|
|
||||||
|
本文件供 Claude Code 在协助开发此项目时参考。所有文档、注释、提交信息均使用中文。
|
||||||
|
|
||||||
|
## 项目概述
|
||||||
|
|
||||||
|
**elec_rag_ui** 是智能电子手册的开源前端工程,**仅支持 Linux(Kylin V10 / Ubuntu)**,**仅支持 ER160(揽月 M50 NPU)和 ER25(芯动力 RPP)硬件**。
|
||||||
|
|
||||||
|
服务进程管理(llama-server / embed-server 的启动停止)**完全委托给 er_backend**(HTTP 17700),前端不直接 spawn 任何推理进程。设置页面不再提供计算卡型号、嵌入模型、生成模型路径等配置入口,这些均由 er_backend Web 管理界面(端口 17700)配置。
|
||||||
|
|
||||||
|
本仓库从 monorepo(electronrag)通过 `scripts/sync-elec-rag-ui.sh` 单向同步而来,历史不保留。
|
||||||
|
|
||||||
|
## 技术栈
|
||||||
|
|
||||||
|
- **框架**:Electron + electron-vite + Vue3 + TypeScript
|
||||||
|
- **UI 组件库**:Naive UI(弹窗/通知/滑出容器)+ 自定义工业风 CSS
|
||||||
|
- **状态管理**:Pinia
|
||||||
|
- **向量数据库**:LanceDB(`@lancedb/lancedb`,嵌入式)
|
||||||
|
- **本地向量化**:`@huggingface/transformers`(底层 `onnxruntime-node`)
|
||||||
|
- **Markdown 渲染**:`marked`
|
||||||
|
- **打包**:electron-builder(Linux AppImage + deb)
|
||||||
|
|
||||||
|
## 文件说明
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├── main/
|
||||||
|
│ ├── index.ts # 主进程:disableHardwareAcceleration, setFullScreen, Menu null
|
||||||
|
│ ├── config.ts # 读写 config.json,含 applyPlatformOverrides
|
||||||
|
│ └── ipc/
|
||||||
|
│ ├── index.ts # 注册所有 IPC 处理器
|
||||||
|
│ ├── indexHandler.ts # 文件选择、文档入库(分块→向量化→写入LanceDB)
|
||||||
|
│ ├── queryHandler.ts # 查询(向量化→检索→流式生成),query:done 含 GeneratorStats
|
||||||
|
│ ├── statusHandler.ts # 数据库状态、配置读写、测试连接(无 Ollama 分支)
|
||||||
|
│ ├── erBackendHandler.ts # er_backend HTTP 17700 代理 IPC(启动/停止/状态)
|
||||||
|
│ ├── hardwareHandler.ts # 硬件探测:hardware:get-detected / hardware:detected
|
||||||
|
│ └── npuHandler.ts # NPU 状态轮询(hm_smi),推送 npu:stats
|
||||||
|
│ └── rag/
|
||||||
|
│ ├── splitter.ts # 递归收集 .md/.txt,自实现递归字符分块
|
||||||
|
│ ├── embedder.ts # 向量化路由:local / llamacpp(无 Ollama 分支)
|
||||||
|
│ ├── localEmbedder.ts # 本地 CPU 向量化(Transformers.js 单例,懒加载)
|
||||||
|
│ ├── generator.ts # 流式生成:仅 llamacpp(接受 llamacppServer 配置)
|
||||||
|
│ ├── hardwareDetector.ts # 探测 hm_smi / rpp-info 判断 er160/er25/none
|
||||||
|
│ └── ollamaModels.ts # (保留文件,此项目不使用 Ollama)
|
||||||
|
├── preload/index.ts # contextBridge 暴露 electronAPI(无 llamaServer IPC)
|
||||||
|
└── renderer/src/
|
||||||
|
├── assets/main.css # CSS 变量(色板)、.i-btn 按钮体系
|
||||||
|
├── App.vue # 根布局:顶栏、主区;服务状态由 SSE 维护
|
||||||
|
├── components/
|
||||||
|
│ ├── IndexPanel.vue # 文档入库面板
|
||||||
|
│ ├── ChatPanel.vue # 问答气泡、Think 块、引用徽章、批量发送
|
||||||
|
│ ├── ContextDrawer.vue # 检索片段抽屉
|
||||||
|
│ ├── SettingsModal.vue # 设置抽屉(仅 RAG 参数 + 语音 + 批量测试)
|
||||||
|
│ └── ServiceControl.vue # 顶栏"启用/停止"按钮(走 er_backend HTTP)
|
||||||
|
├── stores/rag.ts # Pinia store
|
||||||
|
└── types/index.ts # TS 接口 + Window.electronAPI 类型声明
|
||||||
|
```
|
||||||
|
|
||||||
|
## AppConfig 结构
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface AppConfig {
|
||||||
|
embed: {
|
||||||
|
provider: 'local' | 'llamacpp' // 无 Ollama 分支
|
||||||
|
localModelPath: string // 本地 onnx 模型目录
|
||||||
|
// baseUrl / model 由 applyPlatformOverrides 内部推导,不暴露给用户
|
||||||
|
}
|
||||||
|
rag: { chunkSize: number; chunkOverlap: number; topK: number; systemPrompt: string }
|
||||||
|
llamacppServer: {
|
||||||
|
executablePath: string; modelPath: string; port: number; numCtx: number
|
||||||
|
parallelSlots: number; threads: number; gpuLayers: number
|
||||||
|
noWarmup: boolean; extraArgs: string
|
||||||
|
}
|
||||||
|
llamacppEmbed: {
|
||||||
|
modelPath: string; port: number; extraArgs: string
|
||||||
|
}
|
||||||
|
hardware: { cardType: 'er160' | 'er25' }
|
||||||
|
batchQuestions: string[]
|
||||||
|
batchWaitSeconds: number
|
||||||
|
voice: { enabled: boolean; ttsRate: number; inputGain: number; [key: string]: unknown }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**注意**:本项目无 `chat` 字段(生成固定走 llamacppServer),无 `embed.baseUrl` / `embed.model` 用户配置。
|
||||||
|
|
||||||
|
## applyPlatformOverrides 逻辑
|
||||||
|
|
||||||
|
`getConfig()` 末尾调用,按计算卡型号覆盖关键配置:
|
||||||
|
|
||||||
|
**ER160(揽月 M50)**:
|
||||||
|
- `chat.provider = 'llamacpp'`(内部字段)
|
||||||
|
- `embed.provider` 若为 `ollama` 改为 `llamacpp`,`local` 保持不变
|
||||||
|
- `llamacppServer.port = 8081`
|
||||||
|
- `llamacppEmbed.port = 17701`
|
||||||
|
- `embed.baseUrl = 'http://127.0.0.1:17701'`(内部推导)
|
||||||
|
- `extraArgs` 为空时自动补默认值
|
||||||
|
|
||||||
|
**ER25(芯动力 RPP)**:
|
||||||
|
- `embed.provider = 'local'`(强制,本地 CPU)
|
||||||
|
- `llamacppServer.port = 8080`
|
||||||
|
- `extraArgs` 为空时补 `'--ctk bf16 --ctv bf16 --no-warmup -ub 128'`
|
||||||
|
|
||||||
|
## SettingsModal.vue 结构(精简后)
|
||||||
|
|
||||||
|
```
|
||||||
|
NDrawer (width=900)
|
||||||
|
├── .s-tabs 'index' | 'basic' | 'voice' | 'batch' | 'log'
|
||||||
|
├── 文档入库 <IndexPanel />
|
||||||
|
├── 基本设置 检索参数(Top-K + 系统提示词)── 无计算卡/模型配置
|
||||||
|
├── 语音设置 TTS/ASR 参数
|
||||||
|
├── 批量测试 问题列表 + 等待时间滑块
|
||||||
|
└── 服务日志 嵌入/生成服务日志框(isLinux 可见)
|
||||||
|
```
|
||||||
|
|
||||||
|
计算卡型号、嵌入模型路径、生成模型路径等配置已从设置页移除,统一由 er_backend(http://<PAD>:17700)管理。
|
||||||
|
|
||||||
|
## er_backend 集成
|
||||||
|
|
||||||
|
**ServiceControl.vue** 始终走 er_backend HTTP(`useErBackend = true`):
|
||||||
|
- 启动:`POST /api/generate/start`,`POST /api/embed/start`(ER25 跳过)
|
||||||
|
- 停止:`POST /api/generate/stop`,`POST /api/embed/stop`
|
||||||
|
- 状态 SSE:`GET /api/generate/logs`,`GET /api/embed/logs`
|
||||||
|
|
||||||
|
**erBackendHandler.ts** 提供对应 IPC,渲染进程通过 `window.electronAPI.erBackend.*` 调用。
|
||||||
|
|
||||||
|
## 关键技术约定
|
||||||
|
|
||||||
|
### LanceDB 余弦检索(lancedb.ts)
|
||||||
|
|
||||||
|
兆芯 KX-U6780A 无 AVX2,LanceDB 预编译包 SIMD 代码触发 `Illegal instruction`。`search()` 改为 `t.query().toArray()` + 纯 JS 余弦相似度:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function toNumberArray(v: unknown): number[] | Float32Array {
|
||||||
|
if (v instanceof Float32Array || Array.isArray(v)) return v
|
||||||
|
if (typeof (v as any)?.toArray === 'function') return (v as any).toArray()
|
||||||
|
return Array.from(v as Iterable<number>)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Arrow Vector 陷阱**:`toArray()` 返回行中的 vector 字段是 Arrow `Vector<FixedSizeList>` 对象,`b[i]` 返回 `undefined`,必须用 `toNumberArray()` 转换。
|
||||||
|
|
||||||
|
### 并发向量化(embedder.ts)
|
||||||
|
|
||||||
|
llamacpp 分支使用 `Promise.all` 并发发送,让服务端有机会攒批送 NPU:
|
||||||
|
```typescript
|
||||||
|
const vectors = await Promise.all(texts.map((t, i) => fetchOne(t, i)))
|
||||||
|
```
|
||||||
|
每条 60 秒超时,失败重试 2 次。
|
||||||
|
|
||||||
|
### Vue Proxy IPC 序列化
|
||||||
|
|
||||||
|
Pinia 响应式对象直接传给 `ipcRenderer.invoke()` 会触发结构化克隆错误。**所有传入 IPC 的响应式对象必须先序列化**:
|
||||||
|
```typescript
|
||||||
|
const plain = JSON.parse(JSON.stringify(reactiveObj))
|
||||||
|
await window.electronAPI.someIpc(plain)
|
||||||
|
```
|
||||||
|
|
||||||
|
### sharp stub
|
||||||
|
|
||||||
|
`stubs/sharp/` 作为 workspace 包(名称 `sharp`,版本 `0.34.5`)满足 `@huggingface/transformers` v4 的硬性 `require("sharp")`,避免打包后崩溃。
|
||||||
|
|
||||||
|
### 原生模块打包
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
asarUnpack: ['node_modules/@lancedb/**', 'node_modules/apache-arrow/**', 'node_modules/onnxruntime-node/**']
|
||||||
|
```
|
||||||
|
|
||||||
|
### 版本号管理
|
||||||
|
|
||||||
|
版本号取自最近 git tag(`git describe --tags --abbrev=0`),无 git 环境时读 `.app-version` 文件(已加入 `.gitignore`,构建前生成)。
|
||||||
|
|
||||||
|
## NPU 状态监控
|
||||||
|
|
||||||
|
每 5 秒调用 `hm_smi --fields DDR_Memory_Free DDR_Memory_Total Core0 Core1 -a`,推送 `npu:stats` 事件:
|
||||||
|
```typescript
|
||||||
|
interface NpuStats { memUsed: number; memTotal: number; temp: number }
|
||||||
|
```
|
||||||
|
仅 `cardType === 'er160'` 时启动轮询;切换为 er25 时停止并推送 `null`。
|
||||||
|
|
||||||
|
## Git LFS 说明
|
||||||
|
|
||||||
|
`models/**/*.onnx` 通过 Git LFS 管理(见 `.gitattributes`)。克隆后必须:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git lfs install
|
||||||
|
GIT_SSL_NO_VERIFY=true git lfs pull # git.drumtm.com TLS 证书已过期,需绕过
|
||||||
|
```
|
||||||
|
|
||||||
|
推送含 LFS 对象的提交:
|
||||||
|
```bash
|
||||||
|
GIT_SSL_NO_VERIFY=true git lfs push origin main
|
||||||
|
git push origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
## Kylin V10 / Ubuntu 适配要点
|
||||||
|
|
||||||
|
1. **禁用硬件加速**:兆芯集显 `MAX_FRAGMENT_UNIFORM_VECTORS=0` 导致 Skia 崩溃,`app.disableHardwareAcceleration()` 在 Linux 下强制启用
|
||||||
|
2. **全屏**:`ready-to-show` 后调用 `setFullScreen(true)`,覆盖任务栏
|
||||||
|
3. **菜单**:`Menu.setApplicationMenu(null)` 移除菜单栏
|
||||||
|
4. **onnxruntime-node**:必须在 `dependencies` 直接声明(不能为传递依赖),用 `require()` 而非 `await import()`
|
||||||
|
5. **npmrc**:`onnxruntime_node_install=skip` 跳过 CUDA EP 下载(NuGet 302 重定向无法跟随)
|
||||||
|
|
||||||
|
## 开发命令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
npm run dev:linux # 开发模式(--no-sandbox)
|
||||||
|
npm run package:kylin # 打包 x64(AppImage + deb)
|
||||||
|
npm run package:kylin:arm64 # 打包 arm64(须在 ARM64 机器上,先安装系统 fpm)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 同步来源
|
||||||
|
|
||||||
|
本仓库由 monorepo(electronrag)通过以下方式单向同步:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 在 monorepo 根目录执行
|
||||||
|
bash scripts/sync-elec-rag-ui.sh "feat: 功能说明"
|
||||||
|
```
|
||||||
|
|
||||||
|
脚本同步 `src/elec_rag_ui/` → 独立仓库根目录,`models/` 和 `docs/` 同步至对应子目录,自动提交并推送(含 LFS 对象)。
|
||||||
|
|
||||||
|
**不要直接向本仓库提交代码**(除紧急修复外),应在 monorepo 的 `src/elec_rag_ui/` 修改后通过同步脚本推送。
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
- 切换嵌入模型后必须重建索引(维度不同)
|
||||||
|
- Vue 响应式对象通过 IPC 传递前必须序列化
|
||||||
|
- PAD 部署使用 `--no-sandbox` 启动(`/usr/bin/智能电子手册` 脚本已内置)
|
||||||
|
- LFS push 须加 `GIT_SSL_NO_VERIFY=true`(git.drumtm.com 证书已过期)
|
||||||
|
- `libva error` 和 `GLSL warning` 为兆芯集显无害日志,可忽略
|
||||||
110
README.md
Normal file
110
README.md
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
# elec_rag_ui — 智能电子手册前端
|
||||||
|
|
||||||
|
基于 Electron + Vue3 + TypeScript + LanceDB 构建的本地化 RAG(检索增强生成)桌面应用。
|
||||||
|
|
||||||
|
**仅支持 Linux(银河麒麟 Kylin V10 / Ubuntu)**,适配 ER160(揽月 M50 NPU)和 ER25(芯动力 RPP)计算卡。服务进程管理委托给 [er_backend](https://git.drumtm.com/EdgeRhythm/er_backend)(HTTP 17700),前端专注 RAG 逻辑与用户交互。
|
||||||
|
|
||||||
|
## 技术栈
|
||||||
|
|
||||||
|
- **框架**:Electron + electron-vite + Vue3 + TypeScript
|
||||||
|
- **UI**:Naive UI(弹窗/通知)+ 自定义工业风 CSS
|
||||||
|
- **状态管理**:Pinia
|
||||||
|
- **向量数据库**:LanceDB(嵌入式,无需额外服务)
|
||||||
|
- **本地向量化**:`@huggingface/transformers` + `onnxruntime-node`
|
||||||
|
- **生成引擎**:llama.cpp server(通过 er_backend 管理)
|
||||||
|
- **Markdown 渲染**:marked
|
||||||
|
|
||||||
|
## 支持硬件
|
||||||
|
|
||||||
|
| 计算卡 | 嵌入方式 | 生成方式 | 生成端口 |
|
||||||
|
|--------|---------|---------|---------|
|
||||||
|
| **ER160**(揽月 M50 NPU) | llamacpp NPU(默认)或本地 CPU | llamacpp | 8081 |
|
||||||
|
| **ER25**(芯动力 RPP) | 本地 CPU(固定) | llamacpp | 8080 |
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
elec_rag_ui/
|
||||||
|
├── src/
|
||||||
|
│ ├── main/ # Electron 主进程
|
||||||
|
│ │ ├── index.ts # 入口:全屏启动、菜单禁用、信号处理
|
||||||
|
│ │ ├── config.ts # 配置读写(userData/config.json)
|
||||||
|
│ │ ├── ipc/ # IPC 处理器
|
||||||
|
│ │ └── rag/ # RAG 核心逻辑
|
||||||
|
│ ├── preload/index.ts # contextBridge
|
||||||
|
│ ├── renderer/ # Vue3 前端
|
||||||
|
│ │ └── src/
|
||||||
|
│ │ ├── App.vue # 根布局(顶栏 + 主区)
|
||||||
|
│ │ ├── components/ # ChatPanel / IndexPanel / SettingsModal 等
|
||||||
|
│ │ ├── stores/ # Pinia store
|
||||||
|
│ │ └── types/ # TypeScript 类型定义
|
||||||
|
│ └── batch_questions.txt # 批量测试问题示例
|
||||||
|
├── models/ # 本地模型(onnx 由 Git LFS 管理)
|
||||||
|
│ ├── bge-small-zh-v1.5/ # 嵌入模型(512 维,~24MB)
|
||||||
|
│ ├── bge-base-zh-v1.5/ # 嵌入模型(768 维,~98MB,onnx 需自行下载)
|
||||||
|
│ ├── bge-large-zh-v1.5/ # 嵌入模型(1024 维,~312MB,onnx 需自行下载)
|
||||||
|
│ ├── asr/ # Paraformer 流式语音识别模型(~376MB)
|
||||||
|
│ └── punct/ # 标点恢复模型(~72MB)
|
||||||
|
├── docs/ # 示例文档(用于测试索引与问答)
|
||||||
|
├── resources/ # 应用图标、TTS/ASR worker
|
||||||
|
├── stubs/sharp/ # sharp 原生模块 stub
|
||||||
|
├── package.json
|
||||||
|
├── electron-builder.config.ts
|
||||||
|
└── electron.vite.config.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
## 依赖:er_backend
|
||||||
|
|
||||||
|
本应用依赖 **er_backend**(端口 17700)管理 llama-server 推理服务进程。启动前请确保 er_backend 已运行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# PAD 上
|
||||||
|
cd /opt/er-backend
|
||||||
|
nohup ./node/node manager/index.js &
|
||||||
|
|
||||||
|
# 或通过 systemd(deb 安装后自动配置)
|
||||||
|
systemctl status er-backend
|
||||||
|
```
|
||||||
|
|
||||||
|
er_backend 管理界面:`http://<PAD-IP>:17700`(配置模型路径、启动/停止服务)
|
||||||
|
|
||||||
|
## 模型文件
|
||||||
|
|
||||||
|
`models/` 目录中的 `.onnx` 文件通过 **Git LFS** 管理。克隆后拉取实体文件:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git lfs install
|
||||||
|
GIT_SSL_NO_VERIFY=true git lfs pull
|
||||||
|
```
|
||||||
|
|
||||||
|
**bge-base / bge-large** 的 onnx 文件需自行从 HuggingFace 下载:
|
||||||
|
- [bge-base-zh-v1.5](https://huggingface.co/BAAI/bge-base-zh-v1.5)(下载 `onnx/model_quantized.onnx`,约 98MB)
|
||||||
|
- [bge-large-zh-v1.5](https://huggingface.co/BAAI/bge-large-zh-v1.5)(下载 `onnx/model_quantized.onnx`,约 312MB)
|
||||||
|
|
||||||
|
## 开发模式
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
npm run dev:linux # Linux(--no-sandbox,开发专用)
|
||||||
|
```
|
||||||
|
|
||||||
|
> 开发模式下需要设置 `DISPLAY` 环境变量(有图形界面时自动可用)。
|
||||||
|
|
||||||
|
## 打包
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run package:kylin # Linux x64,产出 AppImage + .deb
|
||||||
|
npm run package:kylin:arm64 # Linux ARM64(在 ARM64 机器上执行)
|
||||||
|
# 须先安装系统 fpm:sudo gem install fpm
|
||||||
|
```
|
||||||
|
|
||||||
|
## 数据存储
|
||||||
|
|
||||||
|
| 内容 | 路径 |
|
||||||
|
|------|------|
|
||||||
|
| 配置文件 | `~/.config/simple-rag-app/config.json` |
|
||||||
|
| 向量数据库 | `~/.config/simple-rag-app/lancedb/` |
|
||||||
|
|
||||||
|
## 许可证
|
||||||
|
|
||||||
|
MIT License
|
||||||
Loading…
x
Reference in New Issue
Block a user