Flutter + FunASR 透析管理系统语音输入技术方案
Flutter + FunASR 透析管理系统语音输入技术方案
整理时间: 2026-02-16 08:38
来源: 群聊消息
整理人: AI助手
摘要
本文档在原技术方案基础上,进一步细化了技术选型:采用 Flutter Desktop (Windows) 作为客户端框架,配合 FunASR 语音识别和 Kimi/Minimax 云端 LLM,通过浏览器插件方案实现精准填表。文档还详细分析了开发过程中可能遇到的挑战及应对策略。
一、技术架构(更新版)
1.1 整体架构图
┌─────────────────────────────────────────────────────────────────┐
│ 医生工作站 │
│ │
│ ┌─────────────────┐ ┌─────────────────────────────────────┐ │
│ │ 透析管理系统 │ │ Flutter 客户端 │ │
│ │ (BS 浏览器) │◄───│ ┌─────────────────────────────┐ │ │
│ │ │ │ │ 悬浮球 UI / 全局热键 │ │ │
│ │ [干体重] │ │ │ 录音控制 (record) │ │ │
│ │ [收缩压] │ │ │ WebSocket Server (本地通信) │ │ │
│ │ [舒张压] │ │ └──────────────┬────────────────┘ │ │
│ │ ... │ │ │ │ │
│ └─────────────────┘ │ │ JSON │ │
│ │ ▼ │ │
│ ┌─────────────────┐ │ ┌─────────────────────────────┐ │ │
│ │ Chrome 插件 │◄───┤ │ FunASR API (本地/内网) │ │ │
│ │ (接收 JSON │ │ │ 语音 → 文字 │ │ │
│ │ DOM 填充) │ │ └──────────────┬──────────────┘ │ │
│ └─────────────────┘ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────────────────────┐ │ │
│ │ │ Kimi/Minimax API (云端) │ │ │
│ │ │ 文字 → 结构化 JSON │ │ │
│ │ └─────────────────────────────┘ │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
1.2 核心通信链路
Flutter 运行在 OS 层,浏览器运行在沙盒中,需要通过本地 WebSocket 通信:
医生点击快捷键(Flutter 捕获)
↓
开始录音(Flutter record 模块)
↓
录音结束 → 发送给 FunASR API → 得到文字
↓
发送文字给 Kimi/Minimax API → 得到 JSON 指标
↓
Flutter 通过 WebSocket 将 JSON 推送给浏览器插件
↓
插件通过 JavaScript 操作 DOM 自动填充表单
二、技术栈清单
| 模块 | 推荐技术 | 说明 |
|---|---|---|
| GUI | Flutter Desktop (Windows) | 跨平台能力强,资源占用远低于 Electron |
| 全局热键 | hotkey_manager | 监听快捷键触发录音 |
| 录音控制 | record | 支持 WebM/M4A/WAV 格式 |
| 本地通信 | shelf_web_socket | 在 Flutter 里开本地 WebSocket Server |
| ASR | FunASR (阿里开源) | 通过 runtime-SDK 封装为 API |
| LLM | Kimi 或 Minimax | 云端处理结构化提取 |
| 浏览器插件 | 自研 Chrome Extension | 精准 DOM 填充 |
三、FunASR 方案详解
3.1 为什么选择 FunASR?
| 特性 | 说明 |
|---|---|
| 工业级性能 | Paraformer 模型在中文医疗术语识别上非常精准 |
| 私有化能力 | 支持 Docker 部署,可搬到内网服务器,符合透析数据敏感要求 |
| 流式识别 | 支持边说边出文字,降低延迟感知 |
| 开源免费 | 社区活跃,持续更新 |
3.2 部署方式
方式 A:本地部署(推荐医疗场景)
- 部署在医院内网服务器
- 数据不出院,符合隐私要求
- 需要 GPU 支持(RTX 3060+)
方式 B:封装为后端 API
- 使用 FunASR runtime-SDK 封装
- Flutter 通过 HTTP 调用
- 延迟更低,体验更好
四、云端 LLM 选型
4.1 Kimi vs Minimax 对比
| 特性 | Kimi | Minimax |
|---|---|---|
| 长文本处理 | 极强(128K+) | 强 |
| 指令遵循 | 优秀 | 优秀 |
| 响应速度 | 中等 | 快 |
| JSON 输出 | 稳定 | 稳定 |
| 适合场景 | 病历归纳、复杂对话 | 快速提取、实时响应 |
4.2 结构化提取 Prompt 示例
你是一个透析记录助手。请从这段话中提取以下字段:
字段说明:
- uf: 超滤量 (单位:kg 或 ml)
- bp_high: 收缩压 (单位:mmHg)
- bp_low: 舒张压 (单位:mmHg)
- flow: 透析流量 (单位:ml/min)
- temp: 透析液温度 (单位:℃)
医生口述:"今天给他做了2.5个超滤,流量调到200吧,温度保持36度"
请输出严格 JSON 格式,不要有其他文字:
{"uf": 2.5, "bf": 200, "temp": 36}
五、浏览器插件方案(方案 B)详解
5.1 插件核心功能
| 功能 | 说明 |
|---|---|
| 精准定位 | 通过 id 或 placeholder 匹配输入框(如”干体重”、”收缩压”) |
| 自动填充 | 即使医生光标没点在框里,也能自动填入 |
| 格式校验 | 填入前检查数值是否在合理范围 |
| 高亮提醒 | 异常值在网页上给出高亮提示 |
5.2 插件配置要点
- WebSocket 端口固定(如 127.0.0.1:9999)
- manifest.json 配置正确的 content_security_policy
- 处理 https 页面连接本地 ws 的跨域问题
5.3 DOM 填充示例
// 插件接收 JSON 后的填充逻辑
const fieldMapping = {
'uf': ['超滤量', 'uf', 'ultrafiltration'],
'bp_high': ['收缩压', 'systolic', 'bp_sys'],
'bp_low': ['舒张压', 'diastolic', 'bp_dia'],
'flow': ['透析流量', 'flow', 'blood_flow']
};
function fillForm(data) {
for (const [key, selectors] of Object.entries(fieldMapping)) {
if (data[key] !== undefined) {
// 尝试多种选择器定位
const input = document.querySelector(`input[placeholder*="${selectors[0]}"]`)
|| document.querySelector(`#${selectors[1]}`)
|| document.querySelector(`[name="${selectors[2]}"]`);
if (input) {
input.value = data[key];
input.dispatchEvent(new Event('input', { bubbles: true }));
}
}
}
}
六、开发挑战与应对策略
6.1 全局热键冲突与权限
| 问题 | 对策 |
|---|---|
| 医生电脑预装输入法、医疗软件可能拦截快捷键 | 提供自定义快捷键功能 |
| 高权限 BS 系统页面全局热键可能失效 | 以管理员权限运行 Flutter 应用 |
| 常用快捷键被占用 | 默认使用不常用组合键(如 Ctrl+Shift+V) |
6.2 多屏幕与 DPI 缩放
| 问题 | 对策 |
|---|---|
| 医疗环境多显示器配置常见 | 使用 screen_retriever 库获取精确屏幕参数 |
| 150% 缩放下悬浮窗位置偏移或模糊 | 使用逻辑像素,多分辨率适配测试 |
6.3 音频驱动与采集干扰
| 问题 | 对策 |
|---|---|
| Windows 音频环境复杂(耳麦、扬声器混用) | UI 上提供麦克风设备选择 |
| 采样率转换导致进程卡死 | 录音逻辑放在独立 Isolate 中,防止阻塞主线程 |
| 医生无法判断录音是否生效 | 显示音量波动条 |
6.4 浏览器插件跨域安全限制
| 问题 | 对策 |
|---|---|
| Chrome/Edge 禁止连接不安全 WebSocket | WebSocket 端口固定(如 9999) |
| https 页面连接本地 ws 被拦截 | 插件 manifest 配置 CSP,或使用 Native Messaging |
6.5 资源占用与性能平衡
| 问题 | 对策 |
|---|---|
| 复杂动效导致内存飙升 200MB+ | 使用 –release 编译,清理不必要资源 |
| 后台运行时资源浪费 | 不录音时窗口 Hide 而非 Close,降低心跳频率 |
6.6 FunASR 与 LLM 流式延迟
| 问题 | 对策 |
|---|---|
| 医生对等待敏感(录音5秒 + 处理3秒) | FunASR 流式识别边说边出 |
| 整体延迟影响体验 | ASR 识别出一半时预启动 LLM Prompt 准备 |
6.7 发布与分发(.dll 依赖)
| 问题 | 对策 |
|---|---|
| Flutter Windows 编译后一堆 .dll 文件 | 使用 Inno Setup 打包成单一安装包 |
| 医生电脑缺少 VC++ 运行库 | Inno Setup 中打包 Visual C++ Redistributable |
七、UI 设计建议
7.1 悬浮预览窗
医生说完后,Flutter 弹出半透明浮窗显示识别结果:
┌─────────────────────────────┐
│ 识别结果:超滤 2.5kg,血压 130/80 │
│ │
│ [ ✓ 确认填入 (Enter) ] │
│ [ ✕ 重说 ] │
└─────────────────────────────┘
7.2 状态指示
- 闲置状态:悬浮球灰色半透明
- 录音中:悬浮球红色 + 音量波动
- 处理中:悬浮球黄色旋转
- 完成/错误:绿色勾或红色叹号
八、实施建议
8.1 MVP 路径
第一阶段:最小可行产品
- 实现”按住热键录音 → FunASR 转写 → 文字模拟输入到光标处”
- 验证闭环在透析系统环境下的权限隔离和热键响应
第二阶段:结构化填充
- 加入 Kimi/Minimax LLM 提取 JSON
- 实现浏览器插件 DOM 填充
- 增加”确认环节”防止填错
第三阶段:生产就绪
- 私有化 FunASR 部署
- 优化延迟和体验
- Inno Setup 打包分发
8.2 预计开发周期
| 阶段 | 内容 | 时间 |
|---|---|---|
| MVP | 热键 + 录音 + FunASR 转写 | 3 天 |
| V1.0 | + LLM 提取 + 文字填入 | 3 天 |
| V1.1 | + 浏览器插件 DOM 填充 | 5 天 |
| V2.0 | 优化 + 私有化部署 + 打包 | 7 天 |
| 总计 | 18 天 |
九、需要生成的内容
如需要,可以进一步生成以下内容:
- Flutter 与浏览器插件通信 Demo 代码 - 验证 WebSocket 链路可行性
- Inno Setup 打包脚本模板 - 确保分发不报错
- FunASR API 封装示例 - 本地/内网部署参考
- 完整项目脚手架 - 从零开始的项目结构
整理备注
本文档基于群聊讨论整理,包含了 Flutter + FunASR + LLM 的完整技术方案、开发挑战与应对策略,以及分阶段实施建议。