Mac 随处语音转文字:跨端调用 Windows CapsWriter 全记录

视频链接:https://www.bilibili.com/video/BV1CN8g6yEJ5

依然 claude opus 4.6,但是我总感觉它变得更弱智了一些,然后下面的肯定也会有很多不准确的点。海涵。

比如说 AI 好像很喜欢写什么零点几秒以内,但是事实上语音转文字的场景能差出很多很多。比如说我用云服务器 FRP,那么延迟增加的部分可能是可以预计的,但其他时候语音的长度等各种因素也会大大影响这个时间,所以我只能说,比如说我在我台式机上是非常爽的。然后,如果是跑局域网的话,Mac 端当然也是同样的爽。然后现在 Mac 端当然是全都用 FRP 了,但是也并不能说不爽。只能说如果你只转文字一个字,那你可能会有点觉得有点延迟。但是如果你说一大段话,那你会觉得它转回来的好快。

总的来说,我可以说对我可用,也是 Mac 上面比较有竞争力的一种语音转文字方案。

折腾概览

最终方案:Mac 瘦客户端 + Windows 5060Ti 算力 + FRP 内网穿透

  • 抛弃方案:Mac 本地部署 MLX 模型(1.8GB 常驻内存,8GB Air 撑不住)、Tailscale/ZeroTier 组网(曾失败)、自己手搓客户端尝试与服务端通信(曾失败)
  • 核心架构:Mac 只负责录音和快捷键监听(30MB 内存),以及跑frpc(同样极少内存),音频通过 FRP STCP 加密隧道发给家里 Windows 的 5060Ti 显卡推理,0.3 秒内返回文字
  • 适用场景:一切位置。在家沙发、外出咖啡馆、连手机热点,只要按下快捷键,家里的显卡就在为你打工
  • 踩的坑:Mac 后台权限地狱(麦克风/辅助功能/输入监控三重审查)、launchd 僵尸进程、FRP 心跳超时导致网络切换卡顿、Terminal 窗口残留

整体架构

最终跑通的链路:

Mac 按下快捷键
↓ 本机 127.0.0.1:6016
Mac FRPC (STCP Visitor)
↓ 加密隧道 (Secret Key)
云服务器 FRPS
↓ 加密隧道 (Secret Key)
家里 Windows FRPC (STCP Proxy)
↓ 本机 127.0.0.1:6016
Windows CapsWriter Server
↓ 5060Ti 显卡推理
返回识别文字 → Mac 自动粘贴上屏

为什么不用普通 TCP 映射?
直接把 6016 映射到公网端口(哪怕改成冷门高位端口),本质仍是"裸奔"。CapsWriter 没有密码验证,任何扫到端口的人都能往你显卡里塞音频白嫖算力,甚至构造毒音频触发缓冲区溢出。STCP 模式根本不在公网暴露端口,只有持有 Secret Key 的设备才能建立隧道,黑客的端口扫描器连门都找不到。


一、寻找 Mac 客户端:四条技术路线

CapsWriter 官方只支持 Windows,作者明确表示"没需求不做 Mac 适配"。我当年曾试过手搓客户端,毕竟 C/S 架构的通信应该是最基本的协议,但是完全失败()。这次提前在作者 GitHub issue 里面找了一大圈,发现社区里有三个第三方移植版本,各有取舍:

(提示:除我采用的方案外,其他方案的地址放在文末的技术路线盘点内。)

路线 A:EdgarZhong 的完整 Mac 本地版(未采用)

特点:把整个 Server 搬到 Mac 上,用苹果 MLX 框架跑 Qwen3-ASR 模型。

优势:

  • 完全离线,咖啡馆断网也能用
  • M5 芯片上该作者实测 0.4 秒左右出结果
  • 完整、全量地适配苹果

劣势(致命):

  • 即使最轻量的 4-bit 模型也要占 1.0GB 内存,8-bit 版本 1.8GB
  • 我这台 8+256GB M1 Air,开几个浏览器标签就接近满载,再常驻 1.5GB 的 AI 模型,系统会疯狂 Swap 导致卡顿发热
  • 违背"轻终端"战略:放着家里 5060Ti 0.2 秒的极速算力不用,去榨干 M1 贫弱的性能,本末倒置

路线 B:ChaserSu 的安卓/Linux 跨平台改(参考但未直接用)

特点:不重写推理引擎,只把官方客户端里不支持 Mac 的 keyboard 库换成跨平台的 pynput。

启发:证明了"只改快捷键监听,保留官方通信协议"是可行的。我们后续魔改 Mac 客户端的核心思路就来自这里。

路线 C:new985211 的 Linux 版(参考但未采用)

特点:Linux 的全量版本,以 CapsWriter 2.6 为蓝本,比较新。

启发:类路线 B。

路线 D:francofang 的 macos-port 分支(✅ 最终采用)

特点:fork 官方 2.5 alpha 版本,精准替换了所有不兼容 Mac 的代码(keyboard → pynput、Windows 快捷键 → macOS 快捷键),但 保留了完整的 C/S 分离架构。

为什么选它:

  • 不强制在 Mac 本地跑模型,天然支持连接远端 Server
  • 代码结构与官方 2.5 alpha版本几乎一致,也就同样和我现在真正在用的 2.5 stable 版本差距很小。
  • 有鼠标旁动态波形提示(官方 Windows 版都没有的功能),录音状态一目了然

波形提示,爆赞

项目地址:https://github.com/francofang/CapsWriter-Offline/tree/macos-port


二、Mac 局域网调用:魔改客户端配置

从 GitHub 下载 francofang 的 macos-port 分支源码后,这不是一个打包好的 .app,而是纯 Python 项目。

1. 建立隔离的虚拟环境

cd ~/Projects/CapsWriter-Mac/CapsWriter-Offline-macos-port
python3 -m venv ../.venv
source ../.venv/bin/activate

为什么虚拟环境放在上层目录?
因为源码解压后会嵌套一层 CapsWriter-Offline-macos-port 文件夹,把 .venv 放在外层避免以后更新客户端时误删环境。

2. 安装依赖并补漏

官方文档说直接 pip install -r requirements-client.txt,但实测会漏掉关键依赖,可能是因为版本还是有点差异。正确姿势:

pip install -r requirements-client.txt
pip install rapidfuzz  # 热词匹配必需,官方文档遗漏

3. 修改配置连接 Windows Server

用 Windows 上的同名配置版本替换 config_client.py,找到网络配置部分:

addr = '192.168.1.123'  # 改成你 Windows 设备的局域网 IP
port = '6016'           # CapsWriter Server 默认端口

当然也有一些地方还需要微调,比如说快捷键。可能适合在 Windows 上用的,并不适合在 Mac 上用。这部分可以看下文。总之,我最后是用了cmd_r。

4. 补全缺失配置字段

首次运行可能报错 AttributeError: 'ClientConfig' has no attribute 'hot_rectify'。这是因为 Mac 移植版基于某个特定 commit,需要的配置项比你 Windows 端的多。

在 config_client.py 的热词配置区域手动加上:

hot_rectify = 0.8  # 热词纠错阈值

5. 权限三重审查

macOS 对任何想"模拟按键"或"录制麦克风"的后台脚本极其警惕,当然,现在我们是前台运行,这部分相对还是简单。在后面配 FRP 的时候,相对更恶心一些。你需要在 系统设置 → 隐私与安全性 里给 Terminal 开三个权限。有一部分可能会在你运行是弹窗申请,有一部分需要你去设置你自己开:

  • 麦克风:录音必需
  • 辅助功能 (Accessibility):模拟 Cmd + V 粘贴必需
  • 输入监控 (Input Monitoring):监听快捷键必需

注意这里的权限主体是 Terminal,因为你当前是在终端前台运行。

6. 首次前台运行验证

cd ~/Projects/CapsWriter-Mac/CapsWriter-Offline-macos-port
source ../.venv/bin/activate
python start_client.py

确认终端输出类似:

连接成功
服务端地址:192.168.1.123:6016
当前所用快捷键:Cmd R
使用音频设备:MacBook Air麦克风

然后按一下快捷键说话,松开后应该能在终端看到识别结果。如果能自动粘贴到输入框,局域网方案就算跑通了。

7. 快捷键选择和手感调优

哎,AI 太弱智了,死活不给我写这个点,不知道为什么。明明我丢给他的材料里面有很多我当时的尝试。所以,这个就是我真人写了,会比较粗糙。

当然,说白了也没什么,主要是根据我自己习惯的调优。因为我是习惯用单击模式,就是触发第一次是开始接收音频,触发第2次是结束,然后发回来转文字结果。然而在 Mac 这边的快捷键就比较尴尬。

最后我用的还是右 Command 键,因为 Capslock 键据说有很多冲突的可能性吧,比如说和中英切换,和很多其他东西可能冲突。然后,我用的是 MacBook Air,所以也没有什么其他的空余的按键能给我用了。说实话,我的 F 区也都是用功能键,虽然说按住 Fn 的话也能用 F 键区,但是那毕竟麻烦一些。然后除此之外能用的备选项可能只有右 Option 键,但是右 Option 键好像也有问题,它压根不能作为一个独立的键触发 CapsWriter,所以无奈之下只能用右 Command。

然后,进行了一些尝试优化。其实,反正在我的使用习惯和肌肉记忆下,原本快捷键触发阈值0.3秒,好像也没什么太大冲突。冲突指的是,比如说用 Command 加 Enter ,Command 加 Backspace 这些个很常用的组合;然后,也有部分时候用右边的 Command 键更顺手一些。嗯,但是我还是调了一调,现在我是把阈值设成0.1秒,目前用下来感觉还行。如果设成0.0几秒,很多时候你拼命按都触发不了,因为你按的速度太慢了。如果改的更大,那肯定误触的可能性更高。

这里还是要夸一下,我用的方案的这位作者,做了光标处动态波形显示,非常非常好,这样子能一眼看出来是否已经误触了。然后我的习惯也是,很多时候我会连着说好几分钟的语音,然后再转成文字。那么,最让人难受的点就是,你说了几千个字洋洋洒洒,然后发现他妈的没在录。


三、FRP 内网穿透:安全与便捷的平衡

局域网验证成功后,接下来要解决"外出时如何连回家里的 Server"。

为什么不用 Tailscale/ZeroTier?

我之前尝试过但是,可能是 NAT 类型太差,总是打洞失败,甚至 DERP 延迟高到不可用,所以也懒得搞了。

唯一跑通的只有 FRP (Fast Reverse Proxy),而且我之前已经用它穿透了 Open WebUI 和阅读器,架构可以复用。

STCP vs 普通 TCP:为什么必须用 STCP?

普通 TCP 映射是这样的:

家里 Windows:6016 → 云服务器:53916 (公网暴露)
Mac 直接连 云服务器:53916

安全隐患:

  • 任何人扫到 云服务器IP:53916,就能直接往你的 5060Ti 塞音频
  • CapsWriter 没有密码验证,属于"裸奔"
  • 黑客可以用海量垃圾请求卡死你的显卡(DoS 攻击)
  • 更极端的,构造特殊音频触发底层 C 库的缓冲区溢出漏洞,直接在你 Windows 上执行恶意代码

STCP 的原理:

Windows FRPC 注册一个 STCP Proxy,带 Secret Key
Mac FRPC 以 Visitor 身份连接,必须提供相同的 Secret Key
云服务器 FRPS 只负责验证密钥并转发,不暴露任何公网端口

黑客用端口扫描器根本扫不到这个服务,因为它在公网上"不存在"。


四、Windows 端配置(5 分钟搞定)

1. 修改 FRP 客户端配置

打开 frpc.toml(新版 FRP)或 frpc.ini(旧版),加入:

新版 TOML 格式:

[[proxies]]
name = "CapsWriter_STCP"
type = "stcp"
secretKey = "你的secret key"  # 自定义暗号,越复杂越好
localIP = "127.0.0.1"
localPort = 6016

旧版 INI 格式:

[CapsWriter_STCP]
type = stcp
sk = 你的secret key
local_ip = 127.0.0.1
local_port = 6016

2. 重启 FRP 客户端

如果你的 FRP 是通过任务计划程序自启的:

  1. 按 Win + R,输入 taskschd.msc
  2. 在任务计划程序库里找到 FRP 任务
  3. 右键 → 结束 → 右键 → 运行

如果是前台黑框运行,按 Ctrl + C 停止,然后重新执行启动命令。

不需要改防火墙。 STCP 不对外暴露端口,不需要在 Windows 防火墙或路由器上开放任何端口。


五、Mac 端配置:打通加密隧道

1. 下载 Mac 版 FRP

mkdir -p ~/Projects/FRP-Mac
cd ~/Projects/FRP-Mac
curl -L -o frp.tar.gz https://mirror.ghproxy.com/https://github.com/fatedier/frp/releases/download/v0.56.0/frp_0.56.0_darwin_arm64.tar.gz
tar -zxvf frp.tar.gz
mv frp_0.56.0_darwin_arm64/* .
rm -rf frp_0.56.0_darwin_arm64 frp.tar.gz

发现下不下来,可以手动在浏览器打开https://github.com/fatedier/frp/releases/download/v0.56.0/frp_0.56.0_darwin_arm64.tar.gz,大概率就能跳出下载。

2. 配置 Mac 端 FRPC

创建 frpc.toml:

nano frpc.toml

填入(替换成你自己的服务器信息):

serverAddr = "你的云服务器公网IP"
serverPort = 7000
auth.token = "你的FRP服务端Token"

# 调快心跳,网络切换时快速重连
transport.heartbeatInterval = 10
transport.heartbeatTimeout = 20

[[visitors]]
name = "CapsWriter_Visitor"
type = "stcp"
serverName = "CapsWriter_STCP"  # 必须和 Windows 端的 name 一致
secretKey = "你的secret key"  # 必须和 Windows 端一致
bindAddr = "127.0.0.1"
bindPort = 6016

为什么要调快心跳?
默认心跳间隔是 90 秒。当你从家里 Wi-Fi 切换到手机热点时,Mac 的 IP 变了,但 FRP 的旧连接还在"傻等",要等将近一分钟才会判定超时并重连。把心跳改成 10 秒后,最多 20 秒就能完成重连,体感上喝口水就恢复了。

3. 修改 CapsWriter 客户端配置

现在隧道已经把远端的 6016 端口"拉"到了 Mac 本地,所以客户端配置改成:

addr = '127.0.0.1'  # 连接本地
port = '6016'

4. 验证外网连通性

关键步骤:把 Mac 断开家里 Wi-Fi,连上手机热点(彻底模拟外网环境)。

先启动 Mac 的 FRP:

cd ~/Projects/FRP-Mac/frp_0.56.0_darwin_arm64
./frpc -c frpc.toml

如果看到 start visitor success,说明隧道已建立。

然后在另一个终端启动 CapsWriter 客户端:

cd ~/Projects/CapsWriter-Mac/CapsWriter-Offline-macos-port
source ../.venv/bin/activate
python start_client.py

找个输入框,按快捷键说话,如果能正常上屏,外网穿透就成功了!


六、后台运行与开机自启:权限地狱

前台运行验证成功后,最后一步是让它"开机自动启动,不出现任何黑窗口"。这是整个折腾过程中最恶心的部分。

人类来了,其实你要说多难也不至于,甚至你这一步完全不做,直接终端搞一个开机自启,然后每次开机重启之后,把对应窗口最小化一下就行了。我之前也是这么做的,但是这次我想要让它最小化,然后脱离终端,就起码是终端可以正常 command + q 关上。与此同时,它继续运行,就机缘巧合下比较折腾。

失败路线 A:直接用 launchd 启动裸 Python(❌)

macOS 的 launchd 相当于 Windows 的系统服务。我一开始写了个 .plist 配置,但遇到致命问题:后台运行时录不到任何有效音频,识别结果始终为空。

原因:

  • 前台从 Terminal 运行时,权限主体是 Terminal(已授权麦克风)
  • 后台由 launchd 直接启动时,权限主体变成了 Python
  • 但 macOS 的"麦克风"权限列表里根本没有这个 Python,它拿不到真实音频

失败路线 B:给各种 Python 路径反复授权(❌)

我尝试过:

  • 把 .venv/bin/python 拖进辅助功能
  • 把 Python.framework 里的真身拖进去
  • 把 python3 拖进去

但麦克风列表里始终无法手动添加 Python(macOS 不允许随便拖文件进这个列表),而且即使加了辅助功能,后台运行仍然录不到声音。

最终方案:nohup 脱离 Terminal(✅)

关键发现:CapsWriter 通过 nohup 脱离 Terminal 后,Terminal 可以彻底退出,CapsWriter 仍能保持麦克风权限。

验证步骤:

nohup "$HOME/Library/Application Support/CapsWriter/run-capswriter.sh" \
  </dev/null >/dev/null 2>&1 &
disown

然后 Cmd + Q 彻底退出 Terminal,再测试录音。如果波形正常、能识别、能上屏,就证明这条路可行。


七、最终启动链:Terminal 点火后自动退出

既然 nohup 验证成功,最后一步是写个 Launcher,让它:

  1. 登录时临时启动 Terminal
  2. 用 nohup 启动 CapsWriter runner
  3. 立即关闭 Terminal 窗口
  4. Terminal.app 真正退出

1. 创建 runner 脚本

mkdir -p "$HOME/Library/Application Support/CapsWriter"
mkdir -p "$HOME/Library/Logs/CapsWriter"

创建 run-capswriter.sh:

nano "$HOME/Library/Application Support/CapsWriter/run-capswriter.sh"

填入:

#!/bin/zsh

PYTHON="/Users/你的用户名/Projects/CapsWriter-Mac/.venv/bin/python"
WORKDIR="/Users/你的用户名/Projects/CapsWriter-Mac/CapsWriter-Offline-macos-port"
ENTRY="$WORKDIR/start_client.py"

STATE_DIR="$HOME/Library/Application Support/CapsWriter"
LOG_DIR="$HOME/Library/Logs/CapsWriter"
PID_FILE="$STATE_DIR/runner.pid"
STOP_FILE="$STATE_DIR/stop"

mkdir -p "$STATE_DIR" "$LOG_DIR"
rm -f "$STOP_FILE"

# 防止重复启动
if [[ -f "$PID_FILE" ]]; then
    OLD_PID="$(cat "$PID_FILE" 2>/dev/null)"
    if [[ -n "$OLD_PID" ]] && kill -0 "$OLD_PID" 2>/dev/null; then
        echo "CapsWriter 已在运行,PID: $OLD_PID"
        exit 0
    fi
fi

echo $$ > "$PID_FILE"

cleanup() { rm -f "$PID_FILE"; }
trap cleanup EXIT
trap 'exit 0' INT TERM  # 注意:删掉 HUP,允许脱离 Terminal

cd "$WORKDIR" || exit 1

# 客户端异常退出时自动重启
while [[ ! -e "$STOP_FILE" ]]; do
    PYTHONUNBUFFERED=1 "$PYTHON" "$ENTRY" \
        >> "$LOG_DIR/client.stdout.log" \
        2>> "$LOG_DIR/client.stderr.log"

    [[ -e "$STOP_FILE" ]] && break
    sleep 3
done

赋予执行权限:

chmod +x "$HOME/Library/Application Support/CapsWriter/run-capswriter.sh"

2. 创建 AppleScript Launcher

nano "$HOME/Library/Application Support/CapsWriter/CapsWriter-Launcher.applescript"

填入:

on run
    set runnerPath to "/Users/你的用户名/Library/Application Support/CapsWriter/run-capswriter.sh"
    set stopPath to "/Users/你的用户名/Library/Application Support/CapsWriter/stop"

    set terminalWasRunning to application "Terminal" is running

    set shellCommand to "rm -f " & quoted form of stopPath & "; " & ¬
        "nohup /bin/zsh " & quoted form of runnerPath & ¬
        " </dev/null >/dev/null 2>&1 &!; exit"

    tell application "Terminal"
        do script shellCommand
    end tell

    delay 3

    if terminalWasRunning is false then
        tell application "Terminal"
            quit
        end tell
    end if
end run

关键点:

  • &! 是 zsh 的"后台运行并立即脱离作业控制"
  • exit 让点火用的 Shell 主动退出,不会终止已脱离的 runner
  • 只有 Launcher 自己拉起了 Terminal,才会在最后退出它

3. 编译成 App

mkdir -p "$HOME/Applications"
osacompile \
  -o "$HOME/Applications/CapsWriter Launcher.app" \
  "$HOME/Library/Application Support/CapsWriter/CapsWriter-Launcher.applescript"

4. 添加到登录项

打开 系统设置 → 通用 → 登录项,点击 +,添加:

/Users/你的用户名/Applications/CapsWriter Launcher.app

(这里可能要用 Command 加 Shift 加 G 来跳转到对应目录,请注意这个目录不是他会默认给你跳转的那个应用列表的目录。)

5. FRP 也做成开机自启

FRP 不需要麦克风权限,可以直接用 launchd 真后台运行。创建:

nano ~/Library/LaunchAgents/com.frp.client.plist

填入:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.frp.client</string>

    <key>WorkingDirectory</key>
    <string>/Users/你的用户名/Projects/FRP-Mac/frp_0.56.0_darwin_arm64</string>

    <key>ProgramArguments</key>
    <array>
        <string>/Users/你的用户名/Projects/FRP-Mac/frp_0.56.0_darwin_arm64/frpc</string>
        <string>-c</string>
        <string>frpc.toml</string>
    </array>

    <key>RunAtLoad</key>
    <true/>

    <key>KeepAlive</key>
    <true/>
</dict>
</plist>

加载:

launchctl load ~/Library/LaunchAgents/com.frp.client.plist

八、最终验收与日常使用

重启后的正确表现

登录 macOS
→ CapsWriter Launcher 启动
→ Terminal 可能闪现 1~2 秒
→ nohup 启动并脱离 runner
→ Terminal 真正退出(Dock 无白点)
→ CapsWriter 独立运行
→ FRPC 由 launchd 后台运行

验收清单:

  • Terminal 完全退出,Dock 无白点
  • 快捷键能触发,鼠标旁出现动态波形
  • 麦克风有实际波形变化(不是静音)
  • 识别结果能自动粘贴上屏
  • pgrep -fl "start_client.py" 只有一个实例
  • 从 Wi-Fi 切换到手机热点,等待 20 秒后仍能正常识别

日常检查命令

查看 CapsWriter 状态:

pgrep -fl "start_client.py"

查看日志:

tail -n 100 "$HOME/Library/Logs/CapsWriter/client.stdout.log"
tail -n 100 "$HOME/Library/Logs/CapsWriter/client.stderr.log"

查看 FRP 状态:

launchctl list | grep com.frp.client

停止 CapsWriter:

touch "$HOME/Library/Application Support/CapsWriter/stop"
pkill -f "start_client.py"

重新启动:

rm -f "$HOME/Library/Application Support/CapsWriter/stop"
open "$HOME/Applications/CapsWriter Launcher.app"

九、为什么不做真正的 CapsWriter.app?

你可能会问:为什么不像 EdgarZhong 那样做一个完整的 .app Bundle,而要搞这么复杂的 nohup + Terminal 方案?

原因:

  1. 做 .app 需要解决的问题更多:

    • 固定 Bundle ID
    • 嵌入 NSMicrophoneUsageDescription 声明
    • 处理代码签名(签名变化会导致权限失效,需要重新授权)
    • 解决"双实例"问题(LaunchServices 和 launchd 的冲突)
    • 启动器与虚拟环境 Python 的路径绑定
  2. 当前方案已经满足需求:

    • Terminal 真正退出,不是隐藏
    • Dock 无白点
    • 开机自启
    • 崩溃自动重启
    • 以后更新 Mac 客户端,只要项目路径不变,启动器不用改
  3. 维护成本更低:

    • 不涉及编译启动器二进制
    • 不需要处理签名和公证
    • 客户端代码更新不影响启动链

如果你真的想做原生 .app,可以参考 EdgarZhong 的仓库,但要做好心理准备:每次重新构建启动器后,权限可能因签名变化而失效,需要重置并重新授权。


十、其他技术路线盘点

虽然我们没有采用,但这些方案对其他人可能有用:

1. 安卓上跑 CapsWriter

ChaserSu 做了一个安卓适配版,原理是在 Termux 或 TinyComputer 里跑 Linux 环境,用 scrcpy 把安卓麦克风透传进去。

适用场景:想用平板当纯语音输入设备,配合蓝牙键盘写小说。

劣势:安卓虚拟 Linux 性能损耗大,模型推理慢,而且 scrcpy 音频透传不稳定。

项目地址:https://github.com/ChaserSu/CapsWriter-Offline-Android

2. Mac 本地跑完整 Server(MLX 方案)

EdgarZhong 的版本,适合:

  • 经常在完全离线环境工作(飞机、地铁)
  • Mac 内存 >= 16GB
  • 不在意 1~2GB 常驻内存占用
  • 愿意为了离线牺牲一些识别速度

我为什么不选:我有稳定的 4G/5G 网络,家里台式机接近随时开机,没必要在 8GB 的 Air 上硬跑模型。

项目地址:https://github.com/EdgarZhong/CapsWriter-for-macOS

3. Linux 上跑 CapsWriter

new985211 的版本,适合:

  • Linux 系统。

我为什么不选:显然我没有 Linux 系统。当然如果没有任何其他项目,那可能我会以这个项目为基础魔改。

项目地址:https://github.com/new985211/CapsWriter-Offline

4. Tailscale / ZeroTier 组网

理论上比 FRP 更优雅(P2P 打洞,低延迟),但我的网络环境下曾全部失败/不好用。如果你的路由器支持 UPnP,且运营商没有多层 NAT,可以优先尝试 Tailscale。

5. TCP 方案上直接暴露公网端口 + 改高位端口

这是我一开始考虑的方案(把 6016 映射到公网的 53916),但被我否决了。理由:

  • 全端口扫描器只需要几小时就能扫完你的所有端口
  • 改端口只能挡住低级爬虫,挡不住真黑客
  • CapsWriter 没有密码验证,属于"裸奔"

如果你只是临时测试,且云服务器安全组限制了只有你自己的 IP 能访问,可以用这个方案快速验证。但长期使用必须上 STCP 或 VPN。


十一、总结与反思

这套方案的核心思想是 算力分离:

  • Mac 只负责轻量级的录音和快捷键监听
  • 所有 AI 推理扔给家里的 5060Ti(0.2~0.3 秒出结果)
  • 通过 FRP STCP 加密隧道连接,黑客扫不到端口
  • 局域网和外网用同一套配置,无需切换

适合你的条件:

  • 家里有一台长时间开机的 Windows 设备
  • 设备有独立显卡(N 卡最佳,A 卡和 I 卡也行)
  • 有一台云服务器(最低配即可,只做流量转发)
  • Mac 是你的移动办公设备,内存 <= 16GB

不适合的情况:

  • 经常完全离线工作(飞机、地铁、山区)
  • 家里设备不能长时间开机
  • Mac 内存 >= 32GB,且不介意常驻 2GB 模型

最终,我的 M1 Air 变成了一台完美的"瘦客户端":开机后不出现任何黑窗口,Dock 干干净净,但只要按下快捷键,家里的显卡就在为我打工。无论是在沙发上、咖啡馆里,还是连着手机热点,体验完全一致。

我是在考虑要不要把这个变成第一个视频的 Wenix,希望你开心~

附录:

CapsWriter 地址:https://github.com/HaujetZhao/CapsWriter-Offline

CapsWriter 官方置顶的第三方适配issue:https://github.com/HaujetZhao/CapsWriter-Offline/issues/399