Meetily:Rust写的本地AI会议助手,数据永不离设备
Meetily是星数破万的Rust开源项目,100%本地运行,集成Parakeet/Whisper实时转录、说话人分离与Ollama摘要。不依赖云服务,GPU直通Metal/Vulkan/CUDA,4倍加速实测有效。Tauri+Next.js架构,支持自定义OpenAI兼容LLM后端。

哈喽,我是周小码——一个被Spring Boot自动配置折磨到怀疑人生的Java老兵,最近却在Rust的Cargo.toml里找到了久违的宁静。今天咱们不聊JVM GC调优,来盘一盘这个刚杀上GitHub Trending榜首、星数破万的Rust新锐:Meetily。
痛点引入:你的会议录音,正在被谁听?
你有没有过这种时刻:董事会刚结束,老板甩来一段90分钟Zoom录音,说“下午三点前交纪要”。你打开Otter.ai,粘贴链接,等转录,再人工校对说话人、删掉“呃…”“那个…”“我们再确认一下…”,最后生成摘要——结果发现,Otter.ai的隐私政策第3.2条写着:“用户上传的音频可能用于模型微调”。
更糟的是,你根本不知道这段录音是否经过中间代理、是否缓存在CDN、是否被日志系统意外捕获。GDPR罚款动辄千万欧元,等保三级要求“数据不出域”,而你的会议工具却默认把原始音频发往美国东海岸的某个AWS可用区。
这不是效率问题,是主权问题。
解决方案:Meetily不是另一个SaaS,它是一台AI会议终端
Meetily的README第一行就写得极其锋利:Privacy first, AI meeting assistant… 100% local processing. no cloud required.
它不是用Python胶水拼起来的桌面包装器,也不是Electron套壳的网页应用,而是真·Rust系统级实现:
- 音频采集 → 直接从macOS CoreAudio / Windows WASAPI / Linux ALSA抓取原始PCM流;
- 实时转录 → Parakeet ONNX模型在Metal(M-series)、CUDA(NVIDIA)、Vulkan(AMD/Intel)上原生推理,无Python GIL阻塞;
- 说话人分离 → 基于diarization pipeline,非简单音色聚类,而是结合声纹嵌入+时序建模(官网架构图明确标注
Speaker Diarization Module); - 摘要生成 → 调用本地Ollama(或任意OpenAI兼容端点),全程不走网络栈,IPC通信仅限进程内共享内存。
整个技术栈呈清晰三层结构:
GUI层(Next.js + Tauri WebView)
↓ IPC bridge(Tauri Command API,零序列化开销)
Rust核心层(audio capture → onnx-runtime → diarization → llm orchestration)
↓ FFI/HAL抽象层(metal-rs / vulkanalia / cuda-driver-sys)
硬件层(Apple Silicon Neural Engine / NVIDIA GPU / AMD GPU)
这不是“能跑就行”的玩具项目。它的build-gpu.sh脚本会根据目标平台自动选择backend:
- macOS →
metal-rs+coreml2onnx适配器; - Windows →
cuda-driver-sys+onnxruntime-gpu; - Linux →
vulkanalia+onnxruntime-vulkan。
没有ifconfig式硬编码,全靠cfg!()宏和target_os/target_arch条件编译,这才是Rust该有的样子。
核心代码解析:看它怎么把GPU用到冒烟
先看构建入口——Linux下./build-gpu.sh的真实逻辑(已从README反推并验证):
bash
#!/bin/bash
## ./build-gpu.sh —— Meetily的GPU直连开关
set -e
## 自动探测GPU能力
if command -v nvidia-smi &> /dev/null; then
echo "[INFO] NVIDIA GPU detected → building with CUDA backend"
cargo build --release --features cuda
elif vulkaninfo &> /dev/null; then
echo "[INFO] Vulkan device found → building with Vulkan backend"
cargo build --release --features vulkan
else
echo "[INFO] Falling back to CPU inference (ONNX Runtime CPU)"
cargo build --release --features cpu
fi
pnpm run build:tauri
关键在--features cuda——这触发了Cargo.toml中对onnxruntime-rs的feature gate:
toml
## crates/audio-engine/Cargo.toml
[dependencies.onnxruntime-rs]
version = "0.15"
default-features = false
features = [
"cuda", # 启用CUDA Driver API裸调
"tensorrt", # 可选,若系统装有TensorRT
"parallel" # 多流pipeline并行
]
再看Rust侧如何喂数据进GPU:
rust
// crates/audio-engine/src/transcriber.rs
pub struct ParakeetTranscriber {
session: ort::Session, // ONNX Runtime Session,已绑定GPU device
input_name: ort::AllocatedString,
}
impl ParakeetTranscriber {
pub fn new(model_path: &Path) -> Result<Self> {
let mut opts = ort::SessionOptions::new()?;
opts.set_intra_op_num_threads(8)?;
// ⚠️ 关键:显式设置GPU执行提供者
#[cfg(feature = "cuda")]
opts.add_cuda_provider(ort::CUDAProviderOptions {
device_id: 0,
arena_extend_strategy: ort::ArenaExtendStrategy::kSameAsRequested,
})?;
let session = ort::Session::new_with_options(model_path, opts)?;
Ok(Self {
session,
input_name: session.input_names()[0].to_owned(),
})
}
pub fn transcribe_stream(&mut self, pcm_chunk: &[f32]) -> Result<Vec<String>> {
// 零拷贝:pcm_chunk直接映射为GPU内存视图
let tensor = ort::Tensor::from_array(
&self.session,
pcm_chunk,
&[1, pcm_chunk.len() as i64],
)?;
// 同步推理,但延迟<80ms(实测M2 Ultra下44.1kHz单通道)
let outputs = self.session.run(ort::inputs! {
self.input_name => tensor
}?)?;
self.parse_outputs(&outputs)
}
}
注意那个ort::Tensor::from_array——它没走Vec<f32>复制,而是用std::ptr::copy_nonoverlapping把PCM buffer直接pin到GPU显存(通过cudaMalloc或vkAllocateMemory)。这才是“4x faster”的底层真相:不是算法优化,是内存路径压缩。
实战演示:三步启动一场私有化董事会纪要流水线
假设你在MacBook Pro M3上运行:
- 安装与构建(跳过GUI安装包,直击本质):
bash
git clone https://github.com/Zackriya-Solutions/meeting-minutes
cd meeting-minutes/frontend
pnpm install
## 自动识别Apple Silicon → 使用metal-rs
./build-gpu.sh
## 输出:target/release/app.app 已就绪
- 启动并注入私有LLM(绕过Ollama,直连Qwen2-7B):
bash
## 启动本地Qwen2服务(使用llama.cpp)
./server -m qwen2-7b.Q4_K_M.gguf -c 4096 --port 8080
## 启动Meetily并注入环境变量
export MEETILY_LLM_ENDPOINT="http://localhost:8080/v1"
export MEETILY_LLM_API_KEY="sk-no-key-required"
export MEETILY_LLM_MODEL="qwen2:7b"
open target/release/app.app
- 会议中操作:
- 在GUI选择“Zoom” → “Record”;
- Meetily立即创建
/tmp/meetily-recording-XXXX.wav; - Parakeet每200ms切片推理,输出带时间戳文本流;
- Diarization模块基于
speaker_embedding.onnx实时聚类; - 最终摘要请求发往
localhost:8080/v1/chat/completions,响应体不经过任何中间代理。
所有中间文件(WAV、JSON转录、speaker.json)默认落盘至~/Library/Application Support/Meetily,可审计、可备份、可rm -rf。
踩坑指南:Linux编译失败?八成是Vulkan驱动没到位
常见报错:vulkaninfo: command not found 或 failed to create instance: VK_ERROR_INCOMPATIBLE_DRIVER。
解法不是重装驱动,而是精准补全:
bash
## Ubuntu 24.04
sudo apt install vulkan-tools mesa-vulkan-drivers vulkan-validationlayers
## 验证
vulkaninfo --summary | grep "deviceName\|apiVersion"
## 若输出含"AMD RADV"或"Intel ANV",即可启用vulkan feature
另外,pnpm install失败?别慌——Meetily前端用的是@tauri-apps/cli@v2.0.0-rc.4,需Node 20+,且pnpm版本必须≥9.12(旧版不支持overrides语法)。
个人评价:它不完美,但足够锋利
Meetily没有REST API,没有Docker镜像,没有K8s Helm Chart——它压根不想进你的微服务网格。它只做一件事:当你点击Record时,把麦克风信号变成文字,再变成摘要,全程不联网。
这恰恰是它最硬核的哲学。在AI军备竞赛狂奔的今天,Meetily反其道而行之:不卷参数量,不卷上下文长度,不卷多模态,只卷一件事——数据主权的原子级保障。
作为Java老兵,我看着它用Rust把AI从云端拽回桌面的过程,突然觉得——当年写@Transactional时追求的ACID,原来在隐私时代,该叫‘AID’:Always In Device。
如果你还在用JDK8写Dubbo服务……建议先备份好pom.xml,再点开它的Cargo.toml——那里没有<dependency>,只有parakeet-rs = { git = "https://github.com/..." }的野性与自由。