Meetily:Rust写的本地AI会议助手,数据永不离设备

109 次阅读 0 点赞 0 评论 12 分钟原创开源项目

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

#rust #ai #privacy #local-first #meeting-assistant #whisper #ollama
Meetily:Rust写的本地AI会议助手,数据永不离设备

哈喽,我是周小码——一个被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显存(通过cudaMallocvkAllocateMemory)。这才是“4x faster”的底层真相:不是算法优化,是内存路径压缩。

实战演示:三步启动一场私有化董事会纪要流水线

假设你在MacBook Pro M3上运行:

  1. 安装与构建(跳过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 已就绪
  1. 启动并注入私有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
  1. 会议中操作
  • 在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 foundfailed 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/..." }的野性与自由。

最后更新:2026-03-25T10:01:50

评论 (0)

发表评论

blog.comments.form.loading
0/500
加载评论中...