wechat-pi-bridge
在微信里用语音指挥本机的 AI 编程助手。发一条语音,它在你电脑上真的执行,结果回到微信。
它能干什么
- 手机上发语音或文字 → 电脑执行 → 微信收结果
- 支持
/new/stop/status内置命令管理会话 - 长任务每 40 秒播报进度,不会静默卡死
- 只有扫码登录的那个微信号能下达指令(
ownerOnly)
架构速查
微信 ──► 腾讯 iLink 长轮询 ──► bridge.mjs ──► pi --mode rpc ──► 本机执行
│
语音:AES 解密 → silk-wasm 解码 → 重采样 16k → whisper-server 识别
回发:Markdown 拍平 → 按 1500 字切段 → sendmessage
各文件职责:
| 文件 | 职责 |
|---|---|
src/bridge.mjs |
主循环:登录态加载、长轮询、消息路由、进度播报、回复切段 |
src/ilink.mjs |
腾讯 iLink Bot 协议:二维码、getUpdates、sendText、sendTyping |
src/login.mjs |
二维码登录(PNG + 终端 ASCII),登录态落 data/accounts.json |
src/pi.mjs |
PiSession:spawn pi --mode rpc,JSONL over stdio,回合结束看 agent_settled |
src/store.mjs |
data/ 下的 token、context_token、getUpdates 游标读写(权限 600) |
src/voice.mjs |
微信 SILK 语音解密解码 + whisper 识别(常驻 server 优先,cli 兜底) |
首次安装
按顺序执行,每步确认通过再往下。参考 README.md。
# 1. 依赖
node -v # 需要 >= 20
pi --version # 没有就 npm i -g @earendil-works/pi-coding-agent
brew install whisper-cpp # 或源码编译
# 2. 模型(约 1.5GB,放仓库 models/ 下)
mkdir -p models
curl -L -o models/ggml-large-v3-turbo.bin \
https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3-turbo.bin
# 3. 装依赖并配置
npm install
./install.sh
# 4. 扫码登录(必须由用户本人拿手机扫,agent 不要尝试代扫)
node src/login.mjs
# 5. 启动
node src/bridge.mjs
登录和扫码是必须用户本人完成的一步。二维码会以 PNG 弹出并打印 ASCII 版,让用户用手机微信扫。agent 不要试图自动化这一步。
日常操作
node src/bridge.mjs # 前台启动,日志到终端和 logs/bridge.log
./scripts/install-launchd.sh # macOS 装成开机自启服务
tail -f logs/bridge.log # 跟踪运行日志
tail -f logs/pi.log # 跟踪 pi 侧的工具调用
检查是否在跑:
pgrep -fl 'src/bridge.mjs' # 主进程
pgrep -fl 'whisper-server' # 语音识别常驻进程
排障
先看 logs/bridge.log,它记了每条消息的收发、语音转写耗时、pi 的工具调用。对照下表:
| 现象 | 原因与处理 |
|---|---|
| 启动即退出,提示「尚未登录」 | 没跑 node src/login.mjs,或 data/accounts.json 被删 |
| 发消息没反应 | 看日志有没有 📥 收到消息。没有就是长轮询断了,看 长轮询错误 行;有但显示 ⛔ 拒绝未授权发送者 就是 sender 不是 owner,确认扫码的号和发消息的号是同一个 |
| 回复「语音识别失败」 | logs/whisper-server.log。常见是模型路径不对、模型没下完、或端口被占 |
| 首次语音特别慢 | 正常,whisper-server 首次加载 1.5GB 模型要约 15 秒。之后常驻内存,单条 1-2 秒 |
| 发出去的消息是乱码或带星号 | src/bridge.mjs 的 toPlainText() 没覆盖到那种 Markdown 语法,补一条正则 |
| 任务跑一半没了 | 撞上 turnTimeoutMs(默认 5 分钟)。调大 config.json 里的值,或让用户拆小任务 |
| 命令挂死不返回 | 子进程在等输入。确认 src/bridge.mjs 的 NON_INTERACTIVE_ENV 覆盖了那个工具需要的环境变量 |
| 同一件事被跑了两遍 | 长轮询重连重放了消息。检查 msg_id 去重是否生效(isDuplicate) |
改这个桥接的注意点
改回复格式 —— 入口是 src/bridge.mjs 的 toPlainText() 和 splitForWeChat()。微信完全不渲染 Markdown,**粗体** 会显示成字面的星号。任何新增的格式化语法都要在这里拍平。
改 agent 行为 —— 不要改 src/bridge.mjs,去改 config.json 的 piArgs。用 --append-system-prompt 定制风格最干净,例如:
{
"piArgs": ["--append-system-prompt", "回复控制在 5 行内,不要复述用户的话。"]
}
配置字段查表 —— config.json 所有字段、默认值、常见配方、环境变量清单都在 docs/CONFIGURATION.md;模板是 config.example.json(由 install.sh 复制成 config.json)。字段没写就用默认值,JSON 写错不会崩,只会在日志里提示一句。
换 agent —— 只改 src/pi.mjs。PiSession 对外只暴露 start/ask/abort/newSession 和 tool-start/tool-end/exited 事件,契约很窄。
换语音识别 —— 只改 src/voice.mjs。已经是双通道:优先常驻 whisper-server 的 HTTP 接口,失败退回一次性 whisper-cli。环境变量 WHISPER_BIN / WHISPER_SERVER_BIN / WHISPER_MODEL 都可覆盖。
处理新消息类型 —— src/bridge.mjs 的 handleMessage()。目前只认文字和语音,其他类型回一句说明。图片/文件要自己加下载和落盘逻辑。
安全红线
这个桥接等于把本机 shell 权限交给了微信背后的人。改动时守住这几条:
ownerOnly默认保持true。改成false等于把 shell 开放给任何能给这个号发消息的人。- 不要以 root 跑。桥接进程的权限就是它执行的每条命令的权限。
NON_INTERACTIVE_ENV里每一条都不要删。少一条,对应工具就会在无人值守时挂死等输入:GIT_EDITOR=true防 git 打开编辑器、GIT_TERMINAL_PROMPT=0防 git 卡在输密码、DEBIAN_FRONTEND=noninteractive防安装器弹交互框。- 系统提示词里必须保留「需要用户手动操作时停下来说明」。否则 agent 会在 sudo 密码提示、权限弹窗上无限等下去。
- 凭据文件保持 600。
data/accounts.json里的 bot token 就是完整控制权。 - 提醒用户:语音识别可能出错。涉及删除、转账、对外发送这类不可逆动作,建议让 agent 先复述确认再执行。
- 账号疑似泄露时,让用户删掉
data/accounts.json重新扫码,旧 token 立刻失效。
验证安装是否成功
三步,缺一不可:
# 1. 长轮询通了(日志出现 bridge 启动,且没有反复报长轮询错误)
node src/bridge.mjs
# 2. 语音链路通了(日志出现 🎤 转写完成 + 耗时)
# 让用户在微信发一条语音
# 3. agent 链路通了(日志出现 [pi] 完成 + 毫秒数)
# 看微信有没有收到执行结果
只有三条都过,才算装好了。任何一条不过,按上面的排障表定位。