10 KiB
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 结构
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 = 8081llamacppEmbed.port = 17701embed.baseUrl = 'http://127.0.0.1:17701'(内部推导)extraArgs为空时自动补默认值
ER25(芯动力 RPP):
embed.provider = 'local'(强制,本地 CPU)llamacppServer.port = 8080extraArgs为空时补'--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://: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 余弦相似度:
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:
const vectors = await Promise.all(texts.map((t, i) => fetchOne(t, i)))
每条 60 秒超时,失败重试 2 次。
Vue Proxy IPC 序列化
Pinia 响应式对象直接传给 ipcRenderer.invoke() 会触发结构化克隆错误。所有传入 IPC 的响应式对象必须先序列化:
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"),避免打包后崩溃。
原生模块打包
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 事件:
interface NpuStats { memUsed: number; memTotal: number; temp: number }
仅 cardType === 'er160' 时启动轮询;切换为 er25 时停止并推送 null。
Git LFS 说明
models/**/*.onnx 通过 Git LFS 管理(见 .gitattributes)。克隆后必须:
git lfs install
GIT_SSL_NO_VERIFY=true git lfs pull # git.drumtm.com TLS 证书已过期,需绕过
推送含 LFS 对象的提交:
GIT_SSL_NO_VERIFY=true git lfs push origin main
git push origin main
Kylin V10 / Ubuntu 适配要点
- 禁用硬件加速:兆芯集显
MAX_FRAGMENT_UNIFORM_VECTORS=0导致 Skia 崩溃,app.disableHardwareAcceleration()在 Linux 下强制启用 - 全屏:
ready-to-show后调用setFullScreen(true),覆盖任务栏 - 菜单:
Menu.setApplicationMenu(null)移除菜单栏 - onnxruntime-node:必须在
dependencies直接声明(不能为传递依赖),用require()而非await import() - npmrc:
onnxruntime_node_install=skip跳过 CUDA EP 下载(NuGet 302 重定向无法跟随)
开发命令
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)通过以下方式单向同步:
# 在 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为兆芯集显无害日志,可忽略