用蓝牙耳机开会,会后自动得到一份结构化的会议纪要,这件事完全可以在自己的 VPS 上免费实现——不需要买带 AI 的专属耳机,也不需要按分钟付费的商业订阅。本文给出三种自建方案(从轻量脚本到可视化平台),覆盖 Google Meet、Zoom、Teams,并支持英文 / 中文 / 日文三种语言的自动识别与摘要。

一、为什么把会议摘要放到 VPS 上

买一副自带 AI 能力的会议耳机(如讯飞 iFLYBUDS、韶音 OpenFit AI)确实方便,但硬件与云端绑定,长期成本和数据归属都由厂商决定;而 Read AI、Otter、Fathom 这类 SaaS 免费额度有限、按时长计费,会议内容还会离开自己的控制范围。

自建的三个直接收益:

  • 零按分钟费用:转录用本地的 faster-whisper,摘要走自有的 OneAPI 网关(可对接 DeepSeek、Gemini Flash、Groq 等免费或低价模型),没有时长上限。
  • 数据自持:音频与转写文本只在自己的 VPS 上处理,不进入第三方云。
  • 完全可控:摘要模板、语言、输出格式都能随时改。

前提是你已经有一台 VPS(CPU 即可,4GB 内存以上更从容)和一套 OneAPI(One-API / New-API)LLM 网关。本文的示例跑在 CPU-only、约 6GB 内存的 VPS 上,用的是 faster-whisper 的 base 模型 + int8 量化。

二、整体架构

VPS 在云端,无法直接”听”到你蓝牙耳机里的声音,所以流程必然是本地录音 → 上传 VPS → 转写 → 摘要:

[蓝牙耳机] --音频--> [本地录音: Mac BlackHole / Win WASAPI Loopback]
      |
      v  (导出 mp3/m4a/wav 到监控文件夹)
[文件夹监控脚本 watchdog] --HTTP 上传--> [VPS: FastAPI /summarize]
      |                                        |
      |                                        v
      |                              [faster-whisper 本地转写]
      |                                        |
      |                                        v
      |                        [OneAPI 网关 → LLM 生成结构化摘要]
      v                                        |
[同目录生成 *_SUMMARY.txt] <-------------------+

核心只有两个组件:VPS 上的一个处理 API,和本地 PC 上的一个文件夹监控脚本。装好之后,你开完会只要把录音丢进一个文件夹,几秒到几分钟后同目录就会出现摘要文本。

三、方案一(推荐):轻量 FastAPI + faster-whisper + OneAPI

最省资源、最可控的路径。VPS 上跑一个无界面的处理服务,本地脚本负责上传。

1. 在 VPS 上准备目录与 docker-compose.yml:

version: '3.8'

services:
  meeting-processor:
    image: python:3.11-slim
    container_name: meeting_processor
    ports:
      - "8000:8000"
    environment:
      - ONEAPI_URL=http://YOUR_ONEAPI_IP:PORT/v1
      - ONEAPI_KEY=your_oneapi_api_key_here
      - LLM_MODEL=deepseek-chat   # 换成 OneAPI 里你偏好的模型
    volumes:
      - ./app:/app
    working_dir: /app
    command: >
      bash -c "pip install fastapi uvicorn requests python-multipart faster-whisper && uvicorn main:app --host 0.0.0.0 --port 8000"
    restart: unless-stopped

2. 在同级 app/ 目录下保存 main.py:

import os
from fastapi import FastAPI, UploadFile, File
import requests
from faster_whisper import WhisperModel

app = FastAPI()

ONEAPI_URL = os.getenv("ONEAPI_URL")
ONEAPI_KEY = os.getenv("ONEAPI_KEY")
LLM_MODEL = os.getenv("LLM_MODEL")

# CPU 低资源使用 base + int8
print("Loading Whisper Model...")
whisper_model = WhisperModel("base", device="cpu", compute_type="int8")

@app.post("/summarize")
async def summarize_meeting(file: UploadFile = File(...)):
    temp_path = f"temp_{file.filename}"
    with open(temp_path, "wb") as f:
        f.write(await file.read())

    # Whisper 自动识别 英文 / 中文 / 日文
    print(f"Transcribing {file.filename}...")
    segments, info = whisper_model.transcribe(temp_path, beam_size=5)
    detected_lang = info.language
    transcript = " ".join([s.text for s in segments])
    os.remove(temp_path)

    headers = {"Authorization": f"Bearer {ONEAPI_KEY}",
               "Content-Type": "application/json"}
    system_instruction = (
        "You are an expert executive secretary. Analyze the transcript, detect the "
        "primary language (English, Japanese, or Chinese) and answer in THAT language. "
        "Structure the output exactly as: 1. Executive Summary / 核心摘要 / 概要, "
        "2. Key Decisions / 关键决策 / 決定事項, "
        "3. Action Items with owners / 待办事项与负责人 / タスクと担当者."
    )
    payload = {
        "model": LLM_MODEL,
        "messages": [
            {"role": "system", "content": system_instruction},
            {"role": "user", "content": f"Transcript:\n{transcript}"},
        ],
        "temperature": 0.2,
    }
    r = requests.post(f"{ONEAPI_URL}/chat/completions", json=payload, headers=headers)
    summary = r.json()["choices"][0]["message"]["content"]
    return {"success": True, "summary": summary, "language": detected_lang}

3. 启动:

docker compose up -d

四、方案二:Dify 或 Lobe Chat(可视化工作流)

如果你更想要一个网页界面,而不是命令行,可以在 VPS 上用 Docker 部署现成平台,并把模型请求指向自己的 OneAPI。

  • Dify:新建一个 Chat App 或 Workflow,在”模型供应商”里选 OpenAI 兼容协议,填入 OneAPI 的 http://<oneapi-ip>:<port>/v1 与 Key;开启语音转文字(可接 Whisper API),在系统提示词里写上会议纪要模板。之后打开网页上传录音即可出摘要。
  • Lobe Chat:一条命令起一个类 ChatGPT 界面,把 OneAPI 作为上游:
docker run -d -p 3210:3210 \
  -e OPENAI_API_KEY=your_oneapi_key_here \
  -e OPENAI_PROXY_URL=http://your-oneapi-ip:port/v1 \
  --name lobe-chat \
  lobehub/lobe-chat

建一个名叫”会议助手”的自定义 Agent,把摘要提示词写进它的系统设定,然后把音频文件直接拖进对话框。

注意:Dify / Lobe Chat 是”人工上传 + 点按钮”的交互式方案,适合偶尔开会、想要界面的人;方案一的脚本是”丢文件自动出结果”,适合每天都开会、追求无感自动化的人。

五、方案三:Nextcloud Assistant(已有网盘时顺手)

如果你的 VPS 本来就用 Nextcloud 做云盘,可以启用它的开源 AI 能力:Voice Recorder 允许在浏览器里直接录音或上传,Assistant 应用通过图形界面调用免费的翻译/摘要接口。好处是全程不碰命令行,代价是相对吃资源、配置链更绕。

六、本地端:Mac 与 Windows 的录音与触发

macOS(推荐 BlackHole,免手写录音)

BlackHole 是免费开源的虚拟声卡,可以把系统声音(对方语音)和你自己的麦克风一起回环录制:

  1. brew install blackhole-2ch,在”音频 MIDI 设置”里新建一个多输出设备,同时勾选你的蓝牙耳机和 BlackHole 2ch,开会时把系统输出切到它。
  2. 用 QuickTime Player 的”新建音频录制”,输入设备选 BlackHole 2ch,录完后导出 m4a/mp3 到监控文件夹。

如果希望连这一步都省掉,可以用 Screenpipe 这类本地工具自动抓取任意应用(浏览器或桌面客户端)的会议音频,再交给 VPS 处理。

Windows

用免费的 Audacity:录音主机选 Windows WASAPI,录音设备选对应的 Loopback,即可同时录到系统声音与麦克风,导出后放进监控文件夹。

文件夹监控脚本(跨平台,自动上传)

在本地 PC 上 pip install watchdog requests,保存为 meeting_watcher.py,按需改路径与 VPS 地址:

import os, time, requests
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

HOME_DIR = os.path.expanduser("~")
WATCH_FOLDER = os.path.join(HOME_DIR, "Downloads", "Meetings")
VPS_API_URL = "http://YOUR_VPS_IP:8000/summarize"

class Handler(FileSystemEventHandler):
    def on_created(self, event):
        if event.is_directory or os.path.basename(event.src_path).startswith('.'):
            return
        path = event.src_path
        if not path.lower().endswith(('.mp3', '.m4a', '.wav')):
            return
        print(f"New meeting detected: {os.path.basename(path)}")

        # 等待文件完全写入(Zoom/Teams/浏览器导出有个过程)
        size = -1
        while True:
            time.sleep(3)
            try:
                cur = os.path.getsize(path)
                if cur == size and cur > 0:
                    break
                size = cur
            except OSError:
                time.sleep(1)

        with open(path, 'rb') as f:
            files = {'file': (os.path.basename(path), f, 'audio/mpeg')}
            r = requests.post(VPS_API_URL, files=files, timeout=900)

        if r.status_code == 200 and r.json().get("success"):
            lang = r.json().get("language", "unknown")
            out = os.path.splitext(path)[0] + "_SUMMARY.txt"
            # UTF-8 写入,保证中文/日文不乱码
            with open(out, "w", encoding="utf-8") as o:
                o.write(r.json()["summary"])
            print(f"Summary generated ({lang}) -> {os.path.basename(out)}")
            # macOS 原生通知(可选)
            os.system("osascript -e 'display notification \"Summary ready!\" "
                      "with title \"Meeting Assistant\"'")
        else:
            print("VPS error:", r.text)

if __name__ == "__main__":
    os.makedirs(WATCH_FOLDER, exist_ok=True)
    obs = Observer()
    obs.schedule(Handler(), path=WATCH_FOLDER, recursive=False)
    obs.start()
    print("Watching:", WATCH_FOLDER)
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        obs.stop()
    obs.join()

在 macOS 上还可以把它做成开机自启的 Launch Agent(~/Library/LaunchAgents/*.plist,RunAtLoad + KeepAlive),不用一直开着终端窗口。

七、Google Meet / Zoom / Teams 各自的触发方式

  • Zoom / Teams(桌面客户端):如果你是主持人,客户端本身就能把录像保存到本地——把它们的默认保存目录直接指到监控文件夹(如 ~/Downloads/Meetings),会议一结束文件落到这里,脚本立刻接手。
  • Google Meet(浏览器):浏览器不会自动把音频落到磁盘。用 Chrome 的 “Audio Capture” 这类免费扩展,或 macOS 上用 QuickTime / Screenpipe 录屏录音,点”停止并保存”到监控文件夹即可。

三条路最终都汇入同一个监控文件夹,脚本不关心音频来自哪个会议软件。

八、三语言(英 / 中 / 日)处理要点

  • faster-whisper 会自动检测语言并返回 info.language,中英日混合会议也能处理;语言分界不清时可显式指定 language="ja" 等参数。
  • 让 LLM 用会议的主语言输出摘要(提示词里写明”detect the primary language and answer in that language”),避免把中文会议总结成英文。
  • 文件名与摘要文件统一用 UTF-8 写入,防止中文/日文乱码;输出文件与音频同名、后缀不同(xxx_SUMMARY.txt),便于对照。

九、避坑与调优

  • CPU 优先用 base / tiny 模型 + compute_type="int8":低配 VPS 上跑大模型会拖垮服务器,准确度与速度取一个平衡点。
  • 给上传设足够长的超时:一小时会议转写需要时间,脚本里 timeout=900 比默认值稳妥。
  • 等待文件写完再上传:录音软件导出是渐进的,脚本里”文件大小连续两次不变”再上传,能避免传到半个文件。
  • 把处理服务放在内网或加访问控制:API 端口不要裸奔在公网,尽量只在 Tailscale 等私有网络里暴露。

十、小结

方案 部署方式 交互 适合场景
FastAPI + Whisper + OneAPI Docker 单容器 丢文件自动出摘要 每天开会、要无感自动化
Dify / Lobe Chat Docker 平台 网页上传/拖拽 偶尔开会、想要界面
Nextcloud Assistant 已有网盘上扩展 图形界面 已用 Nextcloud 做云盘

三者都复用同一套底层能力——本地 faster-whisper 转录、自有 OneAPI 网关出摘要——因此零按分钟成本、无时长上限、数据不出自己的 VPS。先按方案一把最小闭环跑通,之后再按需要加可视化界面或自动化触发,是最省事的路径。