各位开发者朋友大家好。今天想分享一个最近在边缘设备上完成的小项目:在 reComputer Mini 上搭建一套完全离线的语音助手,链路是 ASR → LLM → TTS,也就是把语音识别、大语言模型、语音合成全部放在本地,不依赖任何云端接口。整条链路跑通之后,你可以对着麦克风说一句话,设备会先把语音转成文字,交给本地 LLM 理解并生成回答,最后把回答合成为语音播放出来。
如果你之前尝试过用 API 实现语音助手,一定遇到过几个痛点:网络波动导致服务不可用、接口有延迟、每次调用都有费用,更重要的是语音内容会传到第三方服务器,存在隐私风险。把模型全部部署到本地之后,这些问题基本就消失了。本文会从硬件环境开始,逐步拆解 ASR、LLM、TTS 三个模块的搭建过程,最后给出完整的 Python 串联代码和常见问题排查表。
这个项目适合有 Linux 基础的开发者,需要会一点命令行和 Python,不需要你从零训练模型。看完之后,你应该能够独立在类似的 Jetson 设备上跑通整套离线语音助手,并且知道如何根据设备性能调整模型大小和推理参数。
1. 为什么要做本地语音 LLM:离线语音助手的痛点
很多人第一次做语音助手都是先接云平台:ASR 用云识别服务,LLM 用大模型 API,TTS 用云合成接口。这种方案开发起来确实快,但是一旦到了实际使用场景,问题就会暴露出来。
首先是延迟问题。语音经过上传、云端处理、返回结果,中间经历多次网络请求。即使带宽很好,整个链路的响应时间也往往超过两秒,对话体验非常生硬。如果网络环境较差,比如在弱网环境、地下室、移动交通工具上,延迟会更高,甚至直接请求失败。
其次是隐私与数据安全。语音助手会持续采集环境中的声音,这些音频被上传到云端之后,如何处理、如何存储、是否会被用来优化模型,普通用户往往无法控制。在一些办公环境、实验室、医疗场景中,语音数据属于敏感信息,不允许发送到外部服务器。离线部署是唯一合规的选择。
最后是成本与可控性。云 API 按调用次数收费,如果设备长期运行、频繁交互,一个月下来费用并不低。而且第三方服务可能随时调整版本、变更接口,甚至停服。把模型全部放在本地之后,一次投入硬件成本,后续没有调用费用,模型版本也由自己掌控,想换模型、调参数都很方便。
reComputer Mini 这类设备刚好适合这个场景。它基于 NVIDIA Jetson 平台,体积不大,但自带 GPU 和足够的内存,可以运行量化后的小型 LLM、Whisper 模型、Piper TTS 模型。相比普通树莓派,Jetson 平台在并行计算上有明显优势;相比工作站,它又足够低功耗,可以长时间开机运行。所以把它作为本地语音助手的硬件载体,是很合理的选择。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 整体架构:ASR → LLM → TTS 的完整链路
我们要搭建的语音助手有三个核心模块。
ASR(Automatic Speech Recognition,自动语音识别)负责把麦克风采集到的音频转换成文本。常见开源方案有 Whisper、whisper.cpp、sherpa-onnx 等。在 Jetson 设备上,whisper.cpp 编译简单、模型轻量、支持 CUDA 加速,是比较常用的选择。
LLM(Large Language Model,大语言模型)负责文本理解和回复生成。我们需要一个能在边缘设备上运行的模型,所以一般选择 1B 到 3B 参数量的量化模型,使用 llama.cpp 或 Ollama 这类推理框架部署。llama.cpp 对 Jetson 支持比较友好,可以编译出 CUDA 版本,并在本地启动一个 OpenAI 兼容的 API 服务,方便后续调用。
TTS(Text to Speech,文本语音合成)负责把 LLM 生成的文本转换成语音。轻量级方案包括 Piper、espeak-ng、sherpa-onnx TTS。Piper 的优点是模型小、发音自然度尚可,支持多语言,包括中文;espeak-ng 虽然体积小但机械感强,适合快速测试。如果想追求更好的音质,可以用 sherpa-onnx 加载 VITS 等开源模型,不过配置会复杂一些。
整个链路的工作流程如下:设备通过麦克风录音,得到 WAV 音频文件;ASR 模块将音频转成文本;文本传给本地 LLM 服务,生成回复文本;回复文本交给 TTS 模块合成语音;最后通过音频播放设备播放出来。如果你愿意,还可以在中间加入知识库检索(RAG)或工具调用,让语音助手更智能。
架构上,我推荐把 LLM 放在一个常驻后台服务里,ASR 和 TTS 由主程序按需调用。原因是 LLM 模型加载和上下文初始化耗时较长,如果每次对话都重新加载,延迟会非常高。常驻服务可以复用模型和 KV cache,大幅提升响应速度。
3. 环境准备与硬件说明
在开始之前,需要先确认硬件环境。reComputer Mini 是 Seeed Studio 推出的边缘 AI 设备,常见配置基于 NVIDIA Jetson Orin Nano 系列 SoM,内存有 8GB 或 16GB 等规格。不同批次、不同型号的配置可能不同,请以你手上的设备为准。如果设备预装了 JetPack 系统,那么 NVIDIA 的 CUDA、cuDNN、TensorRT 等基础组件通常已经就绪,我们只需要在此基础上安装 Python 环境和构建工具。
首先检查系统信息。建议先安装 jtop 查看 CPU/GPU 频率、内存占用、温度等信息,这对后续调优很有帮助。
bash复制sudo pip3 install jetson-stats
sudo jtop
在终端里按 1 可以查看基本硬件信息,按 4 查看内存和 CPU 使用情况。然后确认 JetPack 和 CUDA 版本。
bash复制cat /etc/nv_tegra_release
如果命令不存在,可以查看 /etc/nv_tegra_release 文件,或者运行 dpkg -l | grep nvidia 看看相关包。不同 JetPack 版本的路径和包名略有差异,但只要能确认系统镜像正常即可。
接下来安装基础依赖。你需要一个可用的编译环境,llama.cpp 和 whisper.cpp 都需要 CMake 或 Make 编译。
bash复制sudo apt update
sudo apt install -y build-essential git cmake python3-pip python3-dev alsa-utils
alsa-utils 提供 arecord/aplay 命令,用于录音和播放。如果你的设备没有板载麦克风,可以外接 USB 麦克风或音频采集卡。建议使用 16kHz 采样率录制单声道,这是 Whisper 和大多数 ASR 模型的标准输入。
Python 环境建议使用虚拟环境,避免污染系统 Python 环境。可以用 venv 或 miniconda。
bash复制python3 -m venv ~/voice_env
source ~/voice_env/bin/activate
pip install --upgrade pip
后面在虚拟环境里安装 Python 依赖包。如果你之前做过 Jetson 开发,也可以直接用系统的 Python3-pip,但虚拟环境更安全。
4. 让 LLM 在本地跑起来
LLM 是整个链路里计算量最大的模块。在 reComputer Mini 上推荐使用量化后的 GGUF 模型。GGUF 是 llama.cpp 定义的模型格式,它可以保存量化权重,同时附带模型结构信息,加载非常方便。常见的开源模型,如 Qwen、Llama、Phi、Gemma 等,都有社区转换好的 GGUF 版本。这里以一个小型 instruct 模型为例,演示部署流程。
4.1 编译安装 llama.cpp
拉取代码并编译,启用 CUDA 支持以获得更好的推理性能。
bash复制git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
cmake -B build -DGGML_CUDA=ON
cmake --build build --config Release -j4
如果你的 JetPack 版本较新,只需要指定 -DGGML_CUDA=ON 即可。如果编译过程中报 CUDA 相关错误,请确认 nvidia-smi 或 CUDA 环境变量是否正确。编译成功后,llama-server 和 llama-cli 会在 build/bin 目录下。
4.2 获取模型文件
将要使用的 GGUF 模型放到一个目录,比如 ~/models。以常见的小型中文模型为例,模型文件可能是 qwen2.5-1.5b-instruct-q4_k_m.gguf,但你并不需要手动输入这个文件名,去模型社区搜索与自己设备内存匹配的 GGUF 文件即可。选择模型时,建议按照“参数越小越好、量化级别越高越好”的原则。2GB 以下内存的设备选 0.5B~1B 模型;8GB 内存的设备可以尝试 1.5B~3B 模型;16GB 内存的设备甚至能跑 7B 量化模型。
下载完成后,用 ls -lh 查看模型文件大小,确认文件完整。
4.3 启动 OpenAI 兼容服务
llama.cpp 自带 llama-server,可以提供 OpenAI 风格的 /v1/chat/completions 接口。这样我们不需要直接操作模型输入输出,只需要用 HTTP 请求和它通信,非常方便。
bash复制./build/bin/llama-server \
-m ~/models/qwen2.5-1.5b-instruct-q4_k_m.gguf \
--host 127.0.0.1 \
--port 8080 \
--ctx-size 4096 \
--threads 4
参数说明:
-m:指定 GGUF 模型路径。--host 127.0.0.1:只监听本地回环地址,避免局域网内其他设备访问。--port 8080:HTTP 服务端口。--ctx-size 4096:上下文长度,数值越大能记住的对话越多,但显存和内存占用也越高。可以先设 2048 或 4096。--threads 4:CPU 线程数,实际线程数建议根据 CPU 核心数调整。
启动后,日志会打印模型加载信息和服务地址。使用 curl 测试:
bash复制curl -s http://127.0.0.1:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "local-model",
"messages": [
{"role": "user", "content": "你好,介绍一下你自己"}
],
"temperature": 0.7
}'
如果正常,你会收到一段 JSON,其中 choices[0].message.content 就是模型生成的回复内容。model 字段可以随意填,本地服务并不会严格校验模型名。
4.4 性能与参数调优
在实际使用中,可以调整几个关键参数。temperature 控制随机性,0.7 左右适合日常聊天,0.2 左右更适合确定性回答。top_p 也会影响生成多样性,一般设置为 0.9。max_tokens 限制一次回复的最大长度,避免模型无限生成。
另外,--n-gpu-layers 参数可以控制模型层数在 GPU 上的分配比例。默认情况 llama.cpp 可能将所有层加载到 GPU,如果显存不足,可以指定比如 -ngl 20,让部分层留在 CPU。具体数值需要根据模型大小和显存容量测试。可以通过日志中的加载层数来观察。
5. 接入 ASR:离线语音识别
ASR 模块负责把语音变成文字。这里选用 whisper.cpp,它是 OpenAI Whisper 模型的一个轻量化 C/C++ 实现,对边缘设备非常友好,支持量化模型和 CUDA 加速。
5.1 编译 whisper.cpp
bash复制git clone https://github.com/ggerganov/whisper.cpp
cd whisper.cpp
cmake -B build -DGGML_CUDA=ON
cmake --build build --config Release -j4
编译完成后,build/bin 下会有 whisper-cli,部分旧版本叫 main,请以你目录里的实际文件名为准。
5.2 获取 Whisper 模型
whisper.cpp 提供了模型下载脚本。常见的模型有 tiny、base、small、medium 等。体积越小识别越快但准确率越低;体积越大越准确但对硬件要求越高。在 reComputer Mini 上推荐使用 base 或 small。
bash复制cd models
bash download-ggml-model.sh base
脚本会从 Hugging Face 下载对应模型。如果你所在的网络无法访问,也可以手动下载,然后放到 models 目录下。模型下载完成后,在终端测试识别。
bash复制cd ..
./build/bin/whisper-cli -m models/ggml-base.bin -f test.wav -otxt -of result
cat result.txt
如果要识别中文,需要保证音频内容清晰,且采样率为 16kHz 左右。
5.3 录制麦克风音频
为了录制测试音频,可以使用 arecord 命令。假设录音 5 秒,采样率 16kHz,单声道,16 位 PCM:
bash复制arecord -t wav -r 16000 -f S16_LE -c 1 -d 5 input.wav
执行后对着麦克风说话,5 秒后录音结束。然后调用 whisper 识别:
bash复制./build/bin/whisper-cli -m models/ggml-base.bin -f input.wav -otxt -of input
cat input.txt
如果没有识别到文字,先检查录音音量。可以用 alsamixer 调整麦克风采集电平,也可以用 speaker-test 测试播放设备。
5.4 在 Python 中调用 whisper.cpp
主程序一般用 Python 写,我们可以通过 subprocess 调用 whisper-cli,读取输出的文本文件。
python复制import subprocess
import logging
WHISPER_BIN = "/home/pi/whisper.cpp/build/bin/whisper-cli"
WHISPER_MODEL = "/home/pi/whisper.cpp/models/ggml-base.bin"
def transcribe(wav_path: str) -> str:
output_prefix = wav_path.replace(".wav", "_asr")
cmd = [
WHISPER_BIN,
"-m", WHISPER_MODEL,
"-f", wav_path,
"-otxt",
"-of", output_prefix,
"-l", "zh"
]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
logging.error("ASR failed: %s", result.stderr)
raise RuntimeError("ASR failed")
with open(output_prefix + ".txt", "r", encoding="utf-8") as f:
text = f.read().strip()
return text
这里使用 -l zh 指定语言为中文。如果你的模型是多语言模型,这个参数能显著提升中文识别效果。如果你是英文场景,可以不传或传 en。
6. 接入 TTS:让助手开口说话
TTS 模块负责把文本转换成语音。Piper 是一个不错的轻量选择,它采用神经网络模型,针对低功耗设备优化。Piper 支持中文,不过音质相比云端方案会弱一些,但作为离线助手已经足够。
6.1 安装 piper-tts
在 Python 虚拟环境中执行:
bash复制pip install piper-tts
安装完成后,命令行工具 piper 会被放在虚拟环境 bin 目录下。确认版本:
bash复制piper --help
6.2 下载中文语音模型
Piper 模型以 .onnx 和 .onnx.json 文件形式存在。你需要选择中文语音模型,例如 zh_CN-huayan-medium,下载后保存到 ~/models/piper/ 目录。由于模型下载源可能变更,请以官方仓库或模型托管平台中的实际文件为准。
6.3 测试 TTS 合成
bash复制echo "你好,我是本地的语音助手" | \
piper \
--model ~/models/piper/zh_CN-huayan-medium.onnx \
--output_file output.wav
成功后会在当前目录生成 output.wav。用 aplay 播放:
bash复制aplay output.wav
如果发音不对或声音很小,可以尝试调整 --length-scale 和 --noise-scale 参数。--length-scale 控制语速,数值越大语速越慢;--noise-scale 控制合成噪声,适当增大可以让语音更生动,但太大容易引入杂音。
6.4 在 Python 中调用 piper
同样使用 subprocess 调用 piper,传入文本和输出文件。
python复制import subprocess
import logging
PIPER_BIN = "/home/pi/voice_env/bin/piper"
PIPER_MODEL = "/home/pi/models/piper/zh_CN-huayan-medium.onnx"
def synthesize(text: str, wav_path: str) -> None:
cmd = [
PIPER_BIN,
"--model", PIPER_MODEL,
"--output_file", wav_path
]
result = subprocess.run(
cmd,
input=text.encode("utf-8"),
capture_output=True,
text=True
)
if result.returncode != 0:
logging.error("TTS failed: %s", result.stderr)
raise RuntimeError("TTS failed")
注意 input 参数传的是文本,piper 会从标准输入读取内容。如果你要合成的文本比较长,可以适当增加 --length-scale 让语速变快,但不要超过 1.5,否则会不自然。
7. 串起全链路:编写本地语音助手程序
现在三个模块都准备好了,接下来把它们串成一个可交互的语音助手。整体流程是:等待触发录音 → ASR 识别 → LLM 生成回复 → TTS 合成 → 播放。
7.1 项目结构
建议把代码组织成如下结构:
code复制voice_assistant/
├── main.py
├── asr.py
├── llm.py
├── tts.py
└── config.py
为了方便演示,本文先给出一个简化版的 main.py,把所有功能放在一个文件里,你实际项目可以拆成模块。
7.2 完整代码示例
先创建一个配置文件 config.py,统一管理路径和参数:
python复制# config.py
import os
WHISPER_BIN = "/home/pi/whisper.cpp/build/bin/whisper-cli"
WHISPER_MODEL = "/home/pi/whisper.cpp/models/ggml-base.bin"
LLM_URL = "http://127.0.0.1:8080/v1/chat/completions"
LLM_TEMPERATURE = 0.7
LLM_MAX_TOKENS = 256
PIPER_BIN = "/home/pi/voice_env/bin/piper"
PIPER_MODEL = "/home/pi/models/piper/zh_CN-huayan-medium.onnx"
RECORD_DEVICE = None # 默认录音设备
RECORD_SECONDS = 5
RECORD_RATE = 16000
AUDIO_PLAYER = "aplay"
然后是主程序 main.py:
python复制# main.py
import json
import logging
import subprocess
import time
import urllib.request
import config
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
logger = logging.getLogger(__name__)
def record_audio(wav_path: str, duration: int = 5) -> None:
logger.info("开始录音 %d 秒...", duration)
cmd = [
"arecord",
"-t", "wav",
"-r", str(config.RECORD_RATE),
"-f", "S16_LE",
"-c", "1",
"-d", str(duration),
wav_path
]
subprocess.run(cmd, check=True)
logger.info("录音完成: %s", wav_path)
def transcribe(wav_path: str) -> str:
output_prefix = wav_path.replace(".wav", "_asr")
cmd = [
config.WHISPER_BIN,
"-m", config.WHISPER_MODEL,
"-f", wav_path,
"-otxt",
"-of", output_prefix,
"-l", "zh"
]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
logger.error("ASR 失败: %s", result.stderr)
raise RuntimeError("ASR failed")
with open(output_prefix + ".txt", "r", encoding="utf-8") as f:
text = f.read().strip()
logger.info("识别结果: %s", text)
return text
def chat(prompt: str, history: list) -> str:
messages = history[-6:] + [{"role": "user", "content": prompt}]
payload = {
"model": "local-model",
"messages": messages,
"temperature": config.LLM_TEMPERATURE,
"max_tokens": config.LLM_MAX_TOKENS,
"stream": False
}
request_data = json.dumps(payload).encode("utf-8")
req = urllib.request.Request(
config.LLM_URL,
data=request_data,
headers={"Content-Type": "application/json"}
)
with urllib.request.urlopen(req, timeout=60) as resp:
body = json.loads(resp.read().decode("utf-8"))
reply = body["choices"][0]["message"]["content"].strip()
logger.info("LLM 回复: %s", reply)
return reply
def synthesize(text: str, wav_path: str) -> None:
cmd = [
config.PIPER_BIN,
"--model", config.PIPER_MODEL,
"--output_file", wav_path
]
result = subprocess.run(
cmd,
input=text.encode("utf-8"),
capture_output=True,
text=True
)
if result.returncode != 0:
logger.error("TTS 失败: %s", result.stderr)
raise RuntimeError("TTS failed")
logger.info("TTS 合成完成: %s", wav_path)
def play_audio(wav_path: str) -> None:
subprocess.run([config.AUDIO_PLAYER, "-q", wav_path], check=True)
def main():
history = []
logger.info("语音助手已启动,按回车键开始录音(Ctrl+C 退出)")
while True:
try:
input("按回车开始讲...")
record_audio("input.wav", config.RECORD_SECONDS)
user_text = transcribe("input.wav")
if not user_text:
logger.warning("没有识别到有效语音,请重试")
continue
reply = chat(user_text, history)
history.append({"role": "user", "content": user_text})
history.append({"role": "assistant", "content": reply})
synthesize(reply, "output.wav")
play_audio("output.wav")
except KeyboardInterrupt:
logger.info("退出语音助手")
break
except Exception as e:
logger.exception("运行出错: %s", e)
if __name__ == "__main__":
main()
这段代码逻辑很直接:每次等待回车,然后录音、识别、请求 LLM、合成语音、播放。history 保存最近三轮对话,实现简单的上下文记忆,但要注意控制 token 长度,因为小模型的上下文窗口有限。
7.3 运行与验证
在运行前,先确认三件事:llama-server 是否已在后台启动、麦克风是否正常、Piper 模型是否存在。然后运行:
bash复制python main.py
程序等待你按下回车,然后开始录音。你对着麦克风说“你好”,大约 5 秒后,屏幕会打印识别结果和 LLM 的回复,最后播放合成的语音。
如果一切正常,你就拥有了一台完全离线的语音助手。后续可以通过按键触发、语音唤醒词、或者连续监听来改善交互体验,但基础链路已经完成了。
8. 常见问题与排查思路
在实际部署中,你可能会遇到各种问题。下面整理了一份排查表,按“问题现象 → 常见原因 → 解决思路”的顺序,可以快速定位。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 录音没声音 | 麦克风未连接或静音 | 用 alsamixer 检查捕获通道,测试 arecord 生成的文件音量;更换 USB 麦克风 |
| ASR 识别结果是空 | 音频采样率不是 16kHz 或背景噪音大 | 确认录音参数 -r 16000 -f S16_LE -c 1;提高 SNR,或换用 larger 模型 |
| ASR 识别中文不准 | 未指定语言或模型过小 | 加 -l zh;使用 small/medium 模型;保证语音清晰 |
| llama-server 启动时内存不足 | 模型大小超过设备内存 | 换更小的模型;降低量化精度;调整 -ngl 减少 GPU 层数 |
| LLM 回复速度很慢 | 未使用 CUDA 编译或模型过大 | 确认 llama.cpp 使用 -DGGML_CUDA=ON 编译;打开 jtop 查看 GPU 占用;减少上下文长度 |
| TTS 输出是英文/发音怪异 | 模型选错或语言不匹配 | 下载正确的中文模型;检查管道输入文本是否为 UTF-8 |
| piper 命令找不到 | 未在虚拟环境激活 | 使用完整路径 /path/to/venv/bin/piper;确认 pip 安装成功 |
| aplay 播放失败 | 输出设备没有音频 | 检查 ALSA 输出设备,用 aplay -l 查看;设置默认声卡 |
| HTTP 请求超时 | llama-server 未启动 | 用 curl 测试接口;查看服务器日志;确认端口未占用 |
另外,有三个高频坑想特别提醒一下。
第一,不要用 44.1kHz 的录音文件直接喂给 Whisper。虽然 whisper.cpp 内部可能会重新采样,但更稳妥的做法是统一用 16kHz 采样率录音,减少重采样导致的音质损失。
第二,LLM 的上下文窗口不要开太大。小模型在长上下文场景下会变慢,而且历史对话过多会占用大量显存。建议在代码里裁剪历史,保留最近 6 条左右即可。
第三,如果设备发热严重,不要长时间满载运行。可以在系统层面设置 CPU/GPU 最高频率,或者给设备增加散热片、风扇。离线语音助手通常只需在录音和推理时满负荷,平时可以降低功耗。
9. 最佳实践与工程建议
链路跑通只是第一步,真正放到实际项目里还有很多工程细节需要打磨。
9.1 模型选型与内存规划
模型选择的核心指标是“内存占用 + 推理速度 + 准确率”。我建议先在设备上运行 jtop,观察空闲内存,再决定模型大小。以 8GB 内存的 reComputer Mini 为例,Whisper base 大约占用 400MB,Piper 模型不到 100MB,LLM 1.5B Q4 模型大约占用 1GB 显存和 2GB 内存,整体压力不大。如果 LLM 换到 7B Q4 模型,内存可能吃紧,建议预留至少 6GB 可用内存给模型。
9.2 异步流水线
目前的代码是串行执行:录音结束后才识别,识别结束后才请求 LLM。实际上可以改成异步流水线,比如录音的同时做降噪、ASR 识别和 LLM 推理可以并行。使用 Python 的 asyncio 或线程池可以显著降低端到端延迟。更进一步的方案是采用流式语音识别,一边录音一边出文本,但这需要 ASR 支持流式接口,复杂度会提升。
9.3 音频处理与降噪
边缘设备的麦克风往往离人比较远,房间环境也有噪声。建议在录音后先做简单的 VAD(语音活动检测),判断是否有人说话;也可以接入降噪模块,比如 RNNoise。如果只想快速提升效果,可以在安静环境测试,或者购买定向麦克风。
9.4 日志与状态管理
语音助手是一个长期运行的程序,建议输出结构化日志,记录每次对话的耗时、识别文本、LLM 回复、错误信息。这样即使用户说了一些无法识别的内容,也能事后分析。还可以在程序里加入健康检查,比如定时检测 llama-server 是否存活,异常时自动重启。
9.5 安全与隐私
本地部署虽然规避了数据传输问题,但仍要注意音频数据和查询日志本身的安全。如果设备放置在公司或实验室,建议给设备设置强密码;录音文件定期清理,避免长期积累敏感数据。LLM 可能生成不符合预期的内容,可以加一层内容过滤或仅用于可控场景。数据库和文件权限也要遵循最小权限原则,不需要所有用户都能访问。
9.6 性能优化方向
如果你的设备支持 TensorRT,可以尝试将 Whisper 和部分 LLM 算子转为 TensorRT engine,从而获得更快的推理速度。不过 TensorRT 转换过程比较复杂,且不同 JetPack 版本差异较大,建议先把基础链路跑通再考虑。另外,llama.cpp 支持 --flash-attn 或 Flash Attention,可以减少显存占用并加速长上下文推理。编译时也可以开启 -DGGML_CUDA_ENABLE_UNIFIED_MEMORY,在统一内存方案下获得更好的性能,但要注意这依赖 CUDA 版本。
9.7 交互体验打磨
想让语音助手更像真实助理,不只是“说话→对话”。你可以加入唤醒词检测,比如使用 openWakeWord 或 Snowboy,让设备在没有唤醒词时保持静默,避免误触发。也可以加入多轮对话管理、性格设定、知识库检索。如果你想让它回答特定文档内容,可以在 LLM 服务前面接一个 RAG 管道,把文档切成片段并建立向量索引,在用户提问时把相关片段注入到提示词中。
10. 总结与下一步
我在 reComputer Mini 上完整走通了 ASR → LLM → TTS 的离线语音助手链路,验证了这套方案的可行性和性价比。你可以在自己的设备上按本文步骤复现,核心就是把 whishper.cpp 的语音识别、llama.cpp 的 LLM 服务、piper 的 TTS 合成串起来,并用 Python 统一调度。整个过程不需要写一行模型训练代码,也不需要联网调用云端 API,符合隐私与可控需求。
下一步可以做几个方向的扩展:一是优化唤醒机制,从手动回车变成“小助手”这样的语音唤醒词;二是接入知识库,让语音助手能回答私有文档内容;三是调整模型组合,比如换更大的 LLM 或更好的 TTS,比较不同设备上的性能差异;四是在 Jetson 平台上尝试 TensorRT 加速,把延迟压缩到更低。
最后提醒一下:不要急着一次性追求最好的模型。先在当前设备上跑通链路,记录每轮延迟、内存占用、识别准确率,再逐步替换模块。这种边缘语音助手的最佳状态不是“跑得越快越好”,而是“在合适的功耗和成本下稳定可用”。把它当成一个可持续迭代的工具去维护,你会发现它的价值远超预期。
