用户反馈:答案还在流式输出时才打开"语音输出",朗读会从打开那一刻 "之后"的新增量开始,开 TTS 之前已经显示在屏幕上的那部分正文被跳过 没有播报。 根因:TtsPlayer.append() 第一行是 `if (!this.options.enabled) return`, TTS 关闭期间每次 onDelta 调用 append() 都会被直接挡掉,内部的 rawText/ lastSpokenPlain 完全不会累积。toggleTts() 原来只处理了"答案已生成完毕 时打开 TTS"(调用 speakWithAnnouncement 整段重播)这一种情况,没有处理 "生成中途打开"的情况——这时 ttsEnabled 变 true 后,下一次 onDelta 送来 的只是新增的那一小段 delta,append() 在一个空白的 rawText 基础上开始 累积,效果就是只从当前位置往后播。 修复:toggleTts() 新增分支,检测到 store.querying 为 true 时,先播 "开始播报"提示音,再用 append(store.rawAnswer) 把已生成的全文"补喂" 一遍,相当于告诉播放器"这就是从头到现在的内容";之后 onDelta 的增量 在这个基础上正常追加,衔接不重复。 验证方式:真机通过 CDP 直连运行中的页面,用 Input.dispatchMouseEvent 模拟真实点击触发中途开启,在主进程 tts:speak IPC 处理器临时加日志 (验证后已移除),确认实际送去朗读的文本顺序是"开始播报"→"根据手册, 蓄电池的拆卸与安装步骤如下:"→"拆卸:"→"一、旋松蓄电池正极导线端子 螺母并取下端子"→...,正确从头开始、后续衔接无重复无跳字。 过程中也确认了 window.electronAPI 这类 contextBridge 暴露对象默认被 冻结,渲染进程侧没法通过 monkey-patch 拦截调用做验证(赋值会静默失败), 后续类似验证只能从主进程 IPC handler 侧加日志或用其他方式。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QeoAy5KmkHuKWHAktpEsaN
elec_rag_ui — 智能电子手册前端
基于 Electron + Vue3 + TypeScript + LanceDB 构建的本地化 RAG(检索增强生成)桌面应用。
仅支持 Linux(银河麒麟 Kylin V10 / Ubuntu),适配 ER160(揽月 M50 NPU)和 ER25(芯动力 RPP)计算卡。服务进程管理委托给 er_backend(HTTP 17700),前端专注 RAG 逻辑与用户交互。
功能
- 文档问答:导入 Markdown/文本手册,提问后检索相关片段并生成带引用标号的回答,点引用可查看原文片段
- 语音输入:Paraformer 流式识别,说话即转文字填入输入框
- 语音输出:边生成边播报,不用等整段答案写完
- 拍照提问:用摄像头拍下实物,先由多模态模型转成一句话描述,再接入同一套知识库检索——所以答案同样基于手册内容、带引用,而不是模型凭空描述图片。需要 er_backend 当前加载的是多模态模型,否则按钮会自动置灰(换回多模态模型后自动恢复)
- 批量测试:按预设问题列表循环提问,用于压测和效果回归
技术栈
- 框架: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 |
| ER25(芯动力 RPP) | 本地 CPU(固定) | llamacpp |
推理请求(生成 + 嵌入)统一经 er_backend 代理转发(http://127.0.0.1:17700),实际 llama-server 监听哪个端口由 er_backend 后端配置决定,前端不感知、也不需要按计算卡型号推导端口。
目录结构
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 / CameraCapture / SettingsModal 等
│ │ ├── stores/ # Pinia store
│ │ ├── utils/ # TTS 播放器、音频采集、Markdown 处理
│ │ └── types/ # TypeScript 类型定义
│ └── batch_questions.txt # 批量测试问题示例
├── models/ # 本地模型(onnx 由 Git LFS 管理)
│ ├── bge-small-zh-v1.5/ # 嵌入模型(512 维,~24MB),本地 CPU 向量化回退用
│ └── asr/ # Paraformer 流式语音识别模型(~376MB)
├── 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 已运行:
# PAD 上
cd /opt/er-backend
nohup ./node/node manager/index.js &
# 或通过 systemd(deb 安装后自动配置)
systemctl status er-backend
er_backend 管理界面:http://<PAD-IP>:17700(配置模型路径、启动/停止服务)
启用拍照提问
需要两个条件同时满足:
- er_backend 加载的是多模态模型——在管理界面把生成模型配成带 mmproj 的视觉模型(启动命令里应带
--mmproj)。应用会自动探测:不是多模态模型时拍照按钮置灰,换回多模态模型后 5 秒内自动恢复可用,不用重启应用。 - 在「设置 → 基本设置 → 拍照提问」里打开开关(默认关闭,因为不是所有部署都会配视觉模型)。
摄像头用标准 USB UVC 设备即可,无需额外驱动。有多个摄像头时,拍照弹层右上角可以切换。
模型文件
models/ 目录中的 .onnx 文件通过 Git LFS 管理。克隆后拉取实体文件:
git lfs install
GIT_SSL_NO_VERIFY=true git lfs pull
语音识别默认不带自动加标点(标点恢复模型未随包分发);如需要可自行准备 sherpa-onnx punct 模型, 在设置里把"标点恢复模型目录"指过去即可,代码本身已支持该功能。
开发模式
npm install
npm run dev:linux # Linux(--no-sandbox,开发专用)
开发模式下需要设置
DISPLAY环境变量(有图形界面时自动可用)。
打包
npm run package:kylin # Linux x64,产出 AppImage + .deb
npm run package:kylin:arm64:deb # Linux ARM64 deb(推荐,须在 ARM64 机器/真机上执行,已验证可行)
npm run package:kylin:arm64 # Linux ARM64(AppImage + deb),真机 Kylin 上 deb 目标
# 走 electron-builder 自带 fpm 必现 tar failed,
# 用 package:kylin:arm64:deb 代替
数据存储
| 内容 | 路径 |
|---|---|
| 配置文件 | ~/.config/elec_rag_ui/config.json |
| 向量数据库 | ~/.config/elec_rag_ui/lancedb/ |
许可证
MIT License
Description
Languages
F*
42.2%
TypeScript
33.8%
Vue
18.5%
JavaScript
2.4%
CSS
2%
Other
1.1%