视频链接: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 是通过任务计划程序自启的:
- 按
Win + R,输入taskschd.msc - 在任务计划程序库里找到 FRP 任务
- 右键 → 结束 → 右键 → 运行
如果是前台黑框运行,按 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,让它:
- 登录时临时启动 Terminal
- 用
nohup启动 CapsWriter runner - 立即关闭 Terminal 窗口
- 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 方案?
原因:
-
做
.app需要解决的问题更多:- 固定 Bundle ID
- 嵌入
NSMicrophoneUsageDescription声明 - 处理代码签名(签名变化会导致权限失效,需要重新授权)
- 解决"双实例"问题(LaunchServices 和 launchd 的冲突)
- 启动器与虚拟环境 Python 的路径绑定
-
当前方案已经满足需求:
- Terminal 真正退出,不是隐藏
- Dock 无白点
- 开机自启
- 崩溃自动重启
- 以后更新 Mac 客户端,只要项目路径不变,启动器不用改
-
维护成本更低:
- 不涉及编译启动器二进制
- 不需要处理签名和公证
- 客户端代码更新不影响启动链
如果你真的想做原生 .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