Skip to main content

标题

这是个很好的问题!让我先回答你的两个核心问题,然后设计方案。


解答你的两个问题

问题1:如何获取面试官语音并转文字?

方案:系统音频捕获 + 麦克风捕获

在 macOS 上,需要同时捕获两个音频源:

  • 麦克风:你自己(面试者)说话
  • 系统音频(Loopback audio):面试官通过 Zoom/Teams/Meet 传来的声音

macOS 不允许直接录制系统音频,需要借助虚拟音频驱动,常见方案:

  • BlackHole(免费,推荐):虚拟声卡,将系统输出路由到可录制的输入
  • Loopback(付费):更强大的音频路由工具
  • SoundFlower(老牌,已停止维护)

语音转文字方案:

  • Apple Speech Recognition(本地,免费,支持中文/英文,低延迟)
  • Whisper(OpenAI,本地运行精度高,但有延迟)
  • AssemblyAI / Deepgram(云端,实时流式转写,准确率高)

问题2:如何区分面试官和面试者?

方案对比:

方案原理优点缺点
双音频流分离 ⭐推荐麦克风=你,系统音频=对方天然区分,最准确需要配置 BlackHole
说话人识别(Speaker Diarization)AI分析声纹区分多人无需额外配置需要云端API,有延迟
手动标记用户按键切换"谁在说话"最简单需要手动操作
声音方向/设备外放=对方,耳麦=自己简单不稳定

推荐方案:双音频流分离(最可靠)

系统音频(BlackHole) → 转文字 → "面试官" → 发给 Claude AI
麦克风音频 → 转文字 → "面试者" → 显示但不处理

推荐技术栈:Tauri + Rust + React

现在我来设计这个 app 的架构图和 UI 原型:这是一个很有价值的架构设计问题。让我先画出完整的系统架构图,然后给出具体的技术方案:---

完整技术方案

核心原理:天然双流分离

麦克风 (CoreAudio) ──────► Stream A → STT → "面试者" → 仅显示
BlackHole虚拟声卡 ─────► Stream B → STT → "面试官" → 发给 Claude AI

Stream B 就是面试官的声音,天然分离,不需要 AI 做说话人识别


技术栈选择

Tauri 2.0 + React(前端)+ Rust(音频/STT 后端)

模块方案说明
系统音频捕获BlackHole 虚拟声卡免费,需用户一次性安装
麦克风捕获cpal(Rust crate)跨平台音频捕获
语音转文字Apple SFSpeechRecognizer(via Objective-C bridge)本地、实时、支持中英文,无网络延迟
AI 回答Claude API(流式输出)边生成边显示
UI 框架Tauri 2.0 + React原生 macOS 窗口,可置顶悬浮

关键代码思路

Rust 端:同时打开两个音频流

// 伪代码
let mic_stream = open_device("Built-in Microphone"); // 面试者
let sys_stream = open_device("BlackHole 2ch"); // 面试官

// 分别送入各自的 STT pipeline
mic_stream -> stt_pipeline_A -> emit("interviewee_text", text);
sys_stream -> stt_pipeline_B -> emit("interviewer_text", text);
// ↑ 只有这条会发给 Claude

前端路由逻辑

// 面试官的问题 → 发给 Claude
listen("interviewer_text", async ({ payload }) => {
const answer = await callClaude(payload.text);
setAiResponse(answer);
});

// 面试者的话 → 只显示字幕,不处理
listen("interviewee_text", ({ payload }) => {
setSubtitle(payload.text);
});

用户安装流程(一次性)

  1. 用户下载安装你的 App
  2. App 引导用户安装 BlackHole 2ch(一个 pkg 安装包)
  3. 引导用户在 macOS 音频 MIDI 设置 中创建「多输出设备」,同时输出到扬声器 + BlackHole
  4. 配置完成后,面试官声音会同时出现在:扬声器(能听到)+ BlackHole(App 可录制)

可选:备选方案(不需要 BlackHole)

如果你不想要用户安装额外驱动,可以用 AssemblyAI 的 Speaker Diarization

所有音频 → AssemblyAI 实时 API

返回: [Speaker A: "请介绍一下你自己"] ← 识别为面试官
[Speaker B: "我是..."] ← 识别为面试者

缺点是有网络延迟(约 1-2s),且需要付费 API。


推荐路径

  • 第一阶段:先用 BlackHole 双流方案,准确率高、无延迟
  • 第二阶段:加入手动标记功能("当前说话人是谁"的切换按钮),作为备用
  • 长远:如果想支持纯线下面试(面对面),再考虑 Whisper + Speaker Diarization

需要我帮你搭建具体的 Tauri 项目结构或核心代码吗?