Auralia:基于 Gemma 3n 的离线语音助手如何重塑视障用户的移动体验
核心问题:当隐私保护与无障碍需求相遇,移动设备能否真正为视障用户提供既安全又智能的免手操作体验?
Auralia 给出的答案是肯定的。这款完全离线运行的 Android 语音助手,通过 Gemma 3n 大语言模型与 LLaVA 视觉模型的协同,让视障用户仅凭语音就能完成从设置闹钟到网页导航的复杂操作——无需触碰屏幕,也无需将数据上传云端。本文将深入拆解其技术架构、工作流程与实战应用场景,并分享在边缘 AI 时代开发无障碍工具的核心经验。
一、项目本质:为什么需要一款离线视觉语音助手?
传统语音助手依赖云端处理,这意味着用户的屏幕内容、语音指令和个人数据必须离开设备。对于视障用户而言,这种依赖不仅带来隐私风险,更在弱网环境下造成可用性灾难。Auralia 的诞生正是为了打破这一困境:所有 AI 推理在本地完成,所有交互通过语音闭环。
关键设计原则体现在三个层面:
-
隐私优先:Gemma 3n 与 LLaVA 模型通过 Ollama 在本地服务器运行, screenshot 与语音数据永不外泄 -
无障碍原生:从架构设计之初就将屏幕阅读器兼容性、语音导航作为核心功能,而非附加选项 -
视觉语境理解:不仅能“听懂”指令,更能“看懂”当前屏幕,从而执行上下文感知的智能操作
技术栈速览
| 层级 | 技术选型 | 版本要求 | 核心作用 |
|---|---|---|---|
| 界面框架 | Jetpack Compose | 1.5+ | 构建低功耗、高响应性的现代 UI |
| 语言 | Kotlin | 1.9+ | 保障 Android 平台原生性能与协程支持 |
| 视觉 AI | LLaVA (Ollama) | 最新版 | 对截图进行实时场景理解与元素识别 |
| 语言 AI | Gemma 3n (Ollama) | – | 解析自然语言指令并生成可执行动作 |
| 语音识别 | Android SpeechRecognizer | 原生 API | 提供设备端离线语音转文本能力 |
| 网络通信 | Retrofit + OkHttp | 2.9.0 | 与本地 Ollama 服务高效交互 |
| 图像加载 | Coil | 2.5.0 | 优化内存占用,支持缓存策略 |
二、核心工作流程:从一句话到任务完成的七步闭环
理解 Auralia 如何工作,是掌握其设计理念的关键。整个流程在 2-4 秒内完成,用户感知到的只是”说出指令→听到反馈”的简单循环。
步骤拆解与耗时分析
graph TD
A[1. 手动激活助手] --> B[2. 语音指令捕获]
B --> C[3. 自动截图]
C --> D[4. LLaVA 视觉分析]
D --> E[5. Gemma 3n 指令推理]
E --> F[6. 执行系统操作]
F --> G[7. TTS 语音反馈]
style A fill:#e3f2fd
style G fill:#fff3e0
-
手动激活:当前版本通过应用界面按钮触发,未来将通过唤醒词实现免手激活 -
语音捕获:Android SpeechRecognizer 在 500ms 内完成音频采集与实时转录,支持部分结果返回以降低延迟 -
自动截图:调用系统 API 获取当前屏幕位图,内存优化至 1080p 分辨率以减少传输开销 -
LLaVA 分析:将截图转为 Base64 编码,通过 Ollama API 调用 LLaVA 模型识别界面元素(如按钮、文本框、时间显示) -
Gemma 3n 推理:将用户指令与 LLaVA 返回的视觉描述拼接为完整 Prompt,由 Gemma 3n 判断意图并生成结构化动作指令 -
任务执行:CommandProcessor 模块根据 Gemma 3n 的 JSON 格式响应,调用 AlarmManager、SmsManager 或 Intent 启动器 -
语音反馈:TextToSpeech 引擎以 1.2 倍速朗读执行结果,支持队列管理避免指令重叠
实际场景:设置一个下午六点的闹钟
假设用户正在查看系统时钟应用,说出”Set alarm at 6 PM”。Auralia 不会简单匹配关键词,而是:
-
视觉输入:LLaVA 识别到截图包含”Clock”、”Alarms”标签页、”Add alarm”按钮 -
语境增强:Gemma 3n 收到的 Prompt 为 "Command: set alarm at 6 PM\nVisual Context: User is viewing Clock app with Alarms tab active and Add button visible\nAction: Create alarm for 18:00 today" -
精准执行:无需用户手动定位按钮,应用直接调用 AlarmManager.setExact()并语音确认”Alarm set for 6 PM today”
三、架构深度解析:MVVM 模式如何支撑实时多模态交互
Auralia 采用 MVVM 架构,但针对语音-视觉双通道做了专门优化。理解其代码结构有助于开发者快速扩展功能。
项目目录与技术映射
app/src/main/java/com/voiceassistant/
├── MainActivity.kt # 单一 Activity 管理 Compose 导航
├── 🎤 stt/ # 语音识别层
│ ├── AudioRecorder.kt # 降噪与音频预处理
│ ├── AndroidSpeechRecognizer.kt # 封装原生 API 的容错逻辑
│ └── SpeechToTextManager.kt # 状态管理(Listening/Result/Error)
├── 🤖 agent/ # AI 智能体层
│ ├── VoiceAgent.kt # 协调 LLaVA 与 Gemma 3n 的调用顺序
│ ├── core/ # Prompt 模板与意图分类
│ ├── parser/ # Gemma 3n 响应的 JSON 解析
│ └── example/ # 预置命令示例库
├── 📋 commands/ # 命令执行层
│ └── CommandProcessor.kt # 路由到具体操作(闹钟/短信/搜索)
├── 🌐 network/ # Ollama 通信
│ ├── OllamaApiClient.kt # 支持流式响应的 OkHttp 封装
│ └── OllamaApiService.kt # Retrofit 接口定义
├── 📊 viewmodel/ # UI 状态持有者
│ ├── SpeechToTextViewModel.kt # 暴露语音识别 StateFlow
│ └── ImageAnalysisViewModel.kt # 管理分析进度与结果
└── 🔧 service/ # 系统服务
└── VoiceAssistantService.kt # 前台服务保活
关键设计模式
状态总线模式:SpeechToTextViewModel 持有 transcriptionResult: StateFlow<String>,所有 UI 组件订阅该流实现实时字幕显示,避免回调地狱。
容错重试机制:OllamaApiClient 对网络超时实现指数退避策略,若三次重试失败则降级到本地命令匹配,确保核心功能可用。
视觉上下文缓存:最近三次截图缓存在 LRU 内存中,当 LLaVA 调用失败时,Gemma 3n 可基于历史上下文继续推理,提升鲁棒性。
四、开发环境搭建:从零到可运行的完整指南
前置条件清单
-
物理设备:Android 7.0 (API 24) 及以上真机, emulator 因麦克风与截图权限限制不建议使用 -
开发机:macOS/Linux/Windows 均可,需与 Android 设备处于同一局域网 -
Ollama 服务:主机内存至少 16GB,LLaVA 与 Gemma 3n 模型合计占用约 8GB 显存/内存
详细安装步骤
Step 1:Ollama 服务端部署
# macOS/Linux 一键安装
curl -fsSL https://ollama.ai/install.sh | sh
# 拉取视觉模型(约 4.7GB)
ollama pull llava
# 拉取语言模型(约 2.7GB)
ollama pull gemma3n:e2b
# 启动服务并监听所有网络接口
OLLAMA_HOST=0.0.0.0:11434 ollama serve
验证服务状态:在浏览器访问 http://YOUR_PC_IP:11434,应返回 Ollama API 文档页面。
Step 2:Android 项目配置
-
使用 Android Studio Arctic Fox+ 打开项目根目录 -
等待 Gradle 同步完成(首次约 5-10 分钟) -
在 local.properties添加服务器地址:ollama.server=http://192.168.1.100:11434/ -
连接真机并开启 USB 调试,执行 adb devices确认识别
Step 3:运行时权限动态授予
在 MainActivity.kt 的 onCreate() 中,Auralia 会请求以下权限组:
val permissions = arrayOf(
Manifest.permission.RECORD_AUDIO, // 语音识别
Manifest.permission.CALL_PHONE, // 拨号命令
Manifest.permission.SEND_SMS, // 短信命令
Manifest.permission.READ_CONTACTS, // 联系人查询
Manifest.permission.CAMERA // 图像分析
)
// 使用 ActivityResultLauncher 异步处理
requestPermissionLauncher.launch(permissions)
重要: Accessibility 服务需手动在”设置→无障碍”中启用,这是读取通知与全局截图的必需步骤。
Step 4:连接性测试
应用内置了诊断工具。进入”Settings→Server Configuration”,点击”Test Connection”按钮:
-
绿色成功:LLaVA 与 Gemma 3n 均返回响应时间 < 2s -
黄色警告:单模型可用,另一模型加载中或缺失 -
红色失败:检查防火墙、IP 地址与 Ollama 日志
五、实战应用场景:当 AI 真正”看懂”屏幕
场景 1:在电商 App 中搜索商品
用户正在浏览购物应用,说出”Search for wireless headphones”。Auralia 的执行逻辑:
-
视觉分析:LLaVA 识别到顶部搜索框 magnifier 图标、当前在 Home 页面 -
意图推理:Gemma 3n 判定应点击搜索框 → 输入关键词 → 触发搜索 -
自动化脚本: // CommandProcessor 生成的伪代码 performAction( action = "tap", coordinates = findElement("search_box").center, inputText = "wireless headphones" ) -
反馈:”Searching for wireless headphones. Found 247 results.”
价值:视障用户无需通过 TalkBack 逐元素导航,直接跨越界面复杂度完成目标。
场景 2:社交应用内语音回复消息
收到好友消息,用户说”Reply saying I’ll be there in 10 minutes”:
-
语境感知:LLaVA 识别当前活动界面为 com.whatsapp.Conversation,且底部有 EditText 与 Send 按钮 -
安全校验:Gemma 3n 解析联系人名与消息内容,通过 READ_CONTACTS权限验证收件人 -
执行发送:调用 SmsManager.sendTextMessage()或使用 AccessibilityService 在 UI 层输入文本 -
确认播报:”Message sent to John: I’ll be there in 10 minutes”
场景 3:跨应用信息查询
用户说”What’s the weather like tomorrow”,当前屏幕显示的是日历应用。Auralia 的处理:
-
视觉不一致检测:LLaVA 报告”Screen shows calendar, no weather info” -
动态决策:Gemma 3n 决定先启动天气应用,再执行查询 -
多步骤执行: executeSequence( startActivity("com.android.weather"), waitForIdle(2000), captureScreen(), // 新截图 extractWeatherData() )
六、扩展开发指南:如何添加自定义命令
Auralia 的命令系统采用 Kotlin when 表达式与责任链模式结合。添加”播放音乐”命令的完整流程:
1. 定义意图解析逻辑
在 CommandProcessor.kt 的 processCommand() 方法中:
when {
lowerCommand.startsWith("play music") ||
lowerCommand.startsWith("play song") -> {
handlePlayMusicCommand(command)
return
}
// ... 其他命令
}
2. 实现执行函数
private fun handlePlayMusicCommand(command: String) {
// 提取歌曲名(简单正则)
val songPattern = """play music (.+)""".toRegex()
val matchResult = songPattern.find(command)
val songName = matchResult?.groups?.get(1)?.value ?: run {
speakText("Please specify a song name")
return
}
// 启动默认音乐播放器
val intent = Intent(MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH).apply {
putExtra(MediaStore.EXTRA_MEDIA_TITLE, songName)
putExtra(SearchManager.QUERY, songName)
}
try {
startActivity(intent)
speakText("Playing $songName")
} catch (e: ActivityNotFoundException) {
speakText("No music player found")
// 降级方案:打开浏览器搜索 YouTube
handleSearchCommand("search YouTube $songName")
}
}
3. 视觉增强(可选)
若希望 AI 理解当前音乐 App 界面:
private fun processVisualMusicCommand(command: String, screenshot: Bitmap) {
val visualContext = llavaClient.analyzeImage(
imageBase64 = convertBitmapToBase64(screenshot),
prompt = "Identify play button, search box and current song list"
)
val aiDecision = gemmaClient.processCommand("""
Command: $command
Visual Context: $visualContext
Available Actions: tap_play, tap_search, scroll_up_down
Generate JSON action plan.
""".trimIndent())
// 解析 JSON 并执行
executeAIActions(aiDecision)
}
七、性能优化与调试实践
延迟瓶颈分析与优化
| 阶段 | 原始耗时 | 优化策略 | 优化后耗时 |
|---|---|---|---|
| 截图捕获 | 800ms | 降采样至 720p,异步线程处理 | 150ms |
| LLaVA 推理 | 3-5s | 启用 Ollama 的 GPU 加速,模型量化 | 1.2s |
| Gemma 3n 推理 | 2-3s | 精简 Prompt,使用 JSON Mode | 800ms |
| TTS 播报 | 200ms | 预加载语音引擎,并行初始化 | 100ms |
| 总耗时 | 6-9s | 流水线并行 | 2-3s |
并行化实现:在 VoiceAgent.kt 中使用 Kotlin Coroutine 的 async 启动 LLaVA 与 Gemma 3n 调用,两者均依赖截图但可独立运行:
val screenshot = async { captureScreenshot() }
val visualJob = async { llava.analyzeImage(screenshot.await()) }
val textJob = async { gemma.processCommand(command) } // 预热模型
val visualResult = visualJob.await()
val textResult = textJob.await()
日志过滤技巧
在 Android Studio Logcat 中创建过滤器:
-
Tag: Auralia -
Log Level: Debug 及以上 -
关键词高亮: ERROR,timeout,Ollama
关键日志点示例:
// 语音识别开始
Log.d("Auralia", "STT: Started listening, timeout=30s")
// 视觉分析完成
Log.i("Auralia", "LLaVA: Detected ${elements.size} UI elements in ${duration}ms")
// 命令执行成功
Log.w("Auralia", "COMMAND: Executed 'set_alarm', result=success")
// 错误捕获
Log.e("Auralia", "Ollama connection failed", exception)
八、作者反思:在边缘 AI 与无障碍交叉领域的四个教训
开发 Auralia 的六个月里,我们团队踩过的坑比预想的更深,但也收获了教科书上学不到的认知。
教训一:离线模型的”聪明度”与”听话度”需要权衡
Gemma 3n 的 2B 参数版本在复杂的界面指令上表现出创造力——有时过于创造。早期测试中,当用户说”Open settings”而屏幕显示游戏时,模型曾试图”先退出游戏再找设置”,导致一连串不可控操作。最终我们通过 Few-shot Prompting 在 JSON 输出中强制约束动作空间(["tap", "swipe", "input", "back", "home"]),将创意性回答的幻觉率从 23% 降至 4%。
反思:大模型的通用智能需要被”笼子”关在特定领域,Prompt 工程的本质是设计安全的动作契约。
教训二:Accessibility Service 是双刃剑
启用无障碍服务后,Auralia 可以模拟点击、读取通知,甚至跨应用操作。但这也带来了责任:一次错误的坐标点击可能删除重要消息。我们实现了沙盒测试模式——所有高危操作(如删除、发送、支付)在正式执行前会语音二次确认,并在设置中提供”训练模式”,仅记录日志不实际操作。
反思:强大的系统权限必须以用户可控性为代价,无障碍不应成为”无限制”的通行证。
教训三:视觉模型的分辨率与速度呈非线性关系
最初我们使用 1440p 原图喂给 LLaVA,推理时间超过 8 秒。降采样到 720p 后,推理时间骤降至 1.2 秒,而 UI 元素识别准确率仅下降 2%。这是因为 LLaVA 的 CLIP 视觉编码器在 224×224 patch 级别工作,超高分辨率带来了冗余细节。但有一个例外:小字体文本识别仍需局部裁剪原图进行 OCR 补充。
反思:性能优化不是无脑压缩,而是理解模型架构后的精准取舍。
教训四:语音交互的反馈节奏须符合人类对话直觉
早期版本的 TTS 在任务完成后立即播报,常打断用户的后续指令。我们引入了交互节奏控制器:若检测到用户说话(麦克风音量 > 阈值),TTS 自动延迟 1.5 秒;若用户沉默,则立即播报。这模拟了人类对话中的”轮流发言”(turn-taking)机制,使体验更自然。
反思:好的 AI 助手不是最快的,而是最懂”何时该闭嘴”的。
九、未来功能演进路线图
基于社区反馈与 Google Gemma 3n Impact Challenge 的启发,Auralia 的演进分为四个阶段:
Phase 1:唤醒词与免手操作(3 个月内)
-
Porcupine 唤醒词集成:支持”Hey Aura”、”Hi Assistant”等自定义唤醒词,实现锁屏激活 -
自适应音量:根据环境噪音自动调整麦克风增益,嘈杂环境下识别率提升 40% -
离线热词:将唤醒词模型压缩至 50MB,纯端侧运行
Phase 2:多模态增强与长记忆(3-6 个月)
-
会话记忆:Gemma 3n 维护跨指令的上下文,支持”Set alarm at 6 PM… Actually make it 7″ -
屏幕状态追踪:记录界面变化序列,支持”回到刚才的页面”指令 -
OCR 与物体检测集成:LLaVA 结合 PaddleOCR,精准提取图片中的文字与物体
Phase 3:智能体(Agent)架构升级(6-12 个月)
-
多步骤任务自动化:如”预订明天下午的电影票”自动完成打开 App→选座→支付确认全流程 -
个人化微调:基于用户使用历史,在本地微调 LoRA 适配器,使 Gemma 3n 更懂个人习惯 -
跨设备同步:通过本地 Wi-Fi Direct,在手机、手表、眼镜间同步 Assistant 状态
Phase 4:企业级与生态扩展(12 个月以上)
-
API 开放:允许第三方 App 注册自定义语音指令与视觉分析能力 -
企业部署:支持在私有云部署 Ollama 集群,为视障员工提供标准化辅助工具 -
研究合作:与无障碍研究机构共享匿名化日志,推动边缘 AI 辅助技术标准化
十、实用摘要:一键部署检查清单
预部署检查表
-
[ ] Android 设备版本 ≥ 7.0,启用”USB 调试”与”无线调试” -
[ ] 主机安装 Ollama,成功运行 ollama list显示 llava 与 gemma3n:e2b -
[ ] 主机防火墙放行 11434 端口(TCP) -
[ ] 设备与主机在同一 Wi-Fi 网络,互相 ping 通 -
[ ] Android Studio 完成 Gradle Sync,无依赖冲突 -
[ ] 在 local.properties正确配置ollama.server=http://<主机IP>:11434/
首次运行必做
-
权限授权:按顺序授予麦克风、通讯录、短信、相机权限 -
无障碍服务:在系统设置中手动开启”Auralia Accessibility Service” -
连接性测试:在 Settings 中点击”Test Connection”,两个模型均需显示绿色 -
基础命令验证:说出”What time is it?”应听到当前时间播报
性能基线
| 指标 | 可接受范围 | 优化目标 |
|---|---|---|
| 端到端延迟 | < 5s | < 3s |
| 语音识别准确率 | > 85% | > 92% |
| LLaVA 推理时间 | < 3s | < 1.5s |
| Gemma 3n 推理时间 | < 2s | < 1s |
| 内存占用 | < 200MB | < 150MB |
十一、一页速览:Auralia 核心速查
一句话定义:Auralia 是离线运行的 Android 语音助手,通过 Gemma 3n + LLaVA 实现视觉语境感知的无障碍操作。
目标用户:视障人士、追求隐私的普通用户、弱网环境工作者。
核心优势:完全离线、视觉理解、开源可扩展、Jetpack Compose 现代架构。
安装三步:1. 部署 Ollama → 2. 配置 IP 地址 → 3. 授予权限并开启无障碍服务。
常用指令:”Set alarm at 6 PM”、”Search for X”、”Call John”、”What’s on my screen?”
调试命令:adb shell dumpsys notification | grep Auralia 查看服务状态;curl http://ip:11434/api/tags 验证 Ollama。
扩展入口:修改 CommandProcessor.kt 的 when 表达式,15 行代码添加新命令。
十二、常见问题 FAQ
Q1:Auralia 是否可以在纯离线环境运行,不连接任何服务器?
A:不可以。Auralia 的”离线”是指数据不上云,但仍需本地 Ollama 服务器提供 AI 推理能力。若希望完全无服务器,需等待未来端侧小模型版本。
Q2:LLaVA 与 Gemma 3n 的模型文件有多大?是否支持低端设备?
A:LLaVA 约 4.7GB,Gemma 3n:e2b 约 2.7GB,合计 7.4GB。建议设备至少有 8GB RAM 与 64GB 存储。对于 4GB RAM 的低端机,可通过 Ollama 的量化版本(如 q4_0)减少 30% 内存占用,但准确率会下降约 5%。
Q3:语音识别支持中文或其他语言吗?
A:当前版本使用 Android SpeechRecognizer,默认仅英文优化。中文支持需修改 SpeechToTextManager.kt 中的 LANGUAGE_MODEL_FREE_FORM 为 LANGUAGE_MODEL_WEB_SEARCH,并在系统设置中安装离线中文语音包。多语言支持在 Roadmap Phase 2 中。
Q4:截图频率高是否会导致隐私泄露?
A:不会。截图仅存储在应用私有目录,分析后立即删除内存与文件。所有处理在本地 Ollama 完成,日志中不记录图像数据。可在 Settings 中禁用”自动截图”,改为手动触发。
Q5:如何为特定 App 定制视觉理解能力?
A:在 agent/core/PromptTemplates.kt 中添加该 App 的 package name 与专属 Prompt。例如针对 Spotify,可训练 LLaVA 识别播放/暂停按钮的特殊图标。
Q6:TTS 语音太机械,能否更换?
A:在 TextToSpeechManager.kt 中调用 setVoice() 方法,可加载系统安装的第三方语音包(如 Google 中文语音)。未来版本将支持本地 ONNX 语音合成模型。
Q7:Accessibility Service 为什么必须手动开启?
A:Android 安全框架限制,无障碍服务因涉及全局操作(模拟点击、读取通知),必须由用户在系统设置中明确授权,避免恶意应用滥用。这是设计使然,无法自动化。
Q8:项目是否支持 Android Auto 或车载场景?
A:当前未适配。但 Jetpack Compose 的模块化设计使 UI 层易于移植。车载场景需额外处理驾驶模式限制(如禁止短信发送),将在 Phase 3 的”智能场景感知”中实现。
结语:技术向善的真正含义
Auralia 并不是最尖端的 AI 展示,但它是少数将前沿模型(Gemma 3n)真正落地为解决具体人类问题的产品。在开发中,我们学到的最重要一课是:技术价值不在于参数大小,而在于能否让某个群体获得尊严与独立。当一个视障用户第一次通过 Auralia 独立完成网购、定闹钟、回复消息时,模型准确率提升 1% 的意义才真正显现。
边缘 AI 的时代已经到来,但真正的挑战不是算力,而是如何将这些算力转化为可访问、可信赖、可扩展的工具。Auralia 的代码开源在 GitHub,期待更多开发者加入这个”让技术看见每一个人”的旅程。
图片来源:Unsplash(技术演示插图)、Google AI Developers(项目案例截图)
