UNlawrence/hermes-weixin-voice
GitHub: UNlawrence/hermes-weixin-voice
Hermes 微信 Agent 的双向语音 I/O 层,结合神经 STT/TTS 与腾讯 SILK_V3 编解码实现语音消息的收发,并附带对 iLink Bot 出站语音渲染问题的实证分析。
Stars: 1 | Forks: 0
# Hermes Weixin 语音
`hermes-weixin-voice` 是 Hermes 微信 agent 的双向语音 I/O 层:它让 agent 能够**听到**传入的微信语音消息,并**说出**回复发送到聊天中。
它是一个本地 Python 包,将神经 TTS、神经 STT、腾讯 SILK_V3 codec 以及 iLink bot 传输协议结合到一个单一的 pipeline 中。它还通过证据记录了公共 iLink bot 路径在何处停止渲染出站*原生*语音气泡,以及剩余的选项有哪些。
## 它的功能
```
┌──────────────────────── inbound (STT) ────────────────────────┐
WeChat voice message → SILK_V3 → 16 kHz PCM → faster-whisper → text
└───────────────────────────────────────────────────────────────┘
┌──────────────────────── outbound (TTS) ───────────────────────┐
agent reply text → Piper (local) | edge-tts (cloud, free) →
24 kHz PCM → SILK_V3 (Tencent)
↓
AES-128-ECB
↓
iLink CDN upload
↓
ITEM_VOICE / ITEM_FILE send
└───────────────────────────────────────────────────────────────┘
```
### 端到端演示(真实输出)
```
$ python -c "..." # synthesize → encode → transcribe round trip
IN: 今天天气真好,我们一起去公园散步吧
OUT: 今天天氣真好,我們一起去公園散步吧。
duration_ms: 3900
```
相同的 TTS 路径会生成一个结构有效的腾讯 SILK_V3 文件(`\x02#!SILK_V3` 魔数,~24 kHz,持续时间与播放匹配),iLink 层可以将其上传并发送到目标 wxid。
## 状态
| 功能 | 状态 | 备注 |
|---|---|---|
| **TTS**(文本 → 语音) | ✅ 已验证 | Piper(本地,ONNX)或 edge-tts(Microsoft 云,免费)→ 24 kHz 单声道 → SILK_V3 腾讯变体 |
| **STT**(语音 → 文本) | ✅ 已验证 | SILK / WAV / MP3 → faster-whisper(`base`, int8, CPU) |
| **微信文件附件发送** | ✅ 已验证 | `ITEM_FILE` 端到端到达目标聊天 |
| **微信原生语音气泡** | ⚠️ 未解决 | `ITEM_VOICE` API 调用成功,但个人微信客户端无法渲染 — 见[分析](#engineering-findings) |
| **Doctor / 本地诊断** | ✅ 已验证 | ffmpeg、base URL、token、context token 预检 |
测试套件:`27 passed, 1 skipped`(跳过的是一个受网络限制的 TTS 测试,可通过 `HV_RUN_NETWORK_TESTS=1` 运行)。
## 快速开始
macOS:
```
git clone https://github.com/UNlawrence/hermes-weixin-voice-clean.git hermes-voice
cd hermes-voice
./install.command # or: ./scripts/install.sh
```
安装程序会:
- 如果缺失,则安装 `uv`
- 如果可用,则通过 Homebrew 安装 `ffmpeg`
- 安装 `hermes-voice`、`hermes-voice-doctor` 和 `hermes-voice-stt` 命令
- 将 Hermes 技能复制到 `~/.hermes/skills/hermes-voice`
在安装过程中,系统会提示您选择一个 Piper TTS 语音模型(默认:`zh_CN-huayan-medium`,约 63 MB)。首次调用 STT 会从 HuggingFace 下载 faster-whisper `base` 模型(约 145 MB)。这两个缓存都是本地的 — 首次安装后,agent 将**完全离线**运行。
要再次运行设置或稍后更改语音:
```
UV_CACHE_DIR=.uv-cache uv run hermes-voice-setup
```
## 命令
### TTS — 发送合成的回复
```
UV_CACHE_DIR=.uv-cache uv run hermes-voice wxid_xxx "今天天气真好"
```
输出:合成文本,编码为 SILK_V3,通过 iLink 上传,并打印 `{msg_id, duration_ms, silk_size, silk_md5, cleaned_text}`。
### STT — 转录音频文件
```
UV_CACHE_DIR=.uv-cache uv run hermes-voice-stt /path/to/voice.silk --language zh
```
通过 header 字节自动检测 SILK;否则将文件交给 ffmpeg 处理(支持 WAV/MP3/M4A/OGG 等格式)。
### 生成真实的 `.silk` 测试文件
```
UV_CACHE_DIR=.uv-cache uv run python scripts/generate_test_silk.py \
--text "这是一条测试语音" --keep-wav
```
打印 `md5`、`first16_hex`、`duration_ms`。适用于独立于网络路径调试 SILK encoder。
### Doctor — 检查本地前置条件
```
UV_CACHE_DIR=.uv-cache uv run hermes-voice-doctor
UV_CACHE_DIR=.uv-cache uv run hermes-voice-doctor wxid_xxx
```
检查 ffmpeg、iLink base URL、token、Hermes 账户配置,以及可选地检查目标 wxid 是否存在 context token。
### 文件附件回退(验证可靠)
```
UV_CACHE_DIR=.uv-cache uv run hermes-voice wxid_xxx \
--send-audio-file /tmp/voice.wav
```
将音频作为 `ITEM_FILE` 发送。这是目前可靠的交付形态 — 请参阅下方的调查结果。
## Programmatic API
```
import asyncio
from hermes_voice import (
send_voice_from_text, # TTS → SILK → iLink send
transcribe, # SILK/WAV/MP3 bytes → text
)
async def main():
# Outbound
result = await send_voice_from_text("你好,我是 Hermes", "wxid_xxx")
print(result.msg_id, result.duration_ms)
# Inbound
voice_bytes = open("/path/to/wechat_voice.silk", "rb").read()
text = await transcribe(voice_bytes, language="zh")
print(text)
asyncio.run(main())
```
## 工程调查结果
本项目用于追踪通过公共 iLink bot 基础设施的个人微信账号的完整出站语音路径。完整的说明在 [WEIXIN_VOICE_ANALYSIS.md](WEIXIN_VOICE_ANALYSIS.md) 中;简短版本如下:
1. **本地编码是正确的。** 生成的 `.silk` 文件具有有效的腾讯 `SILK_V3` header、预期的持续时间以及稳定的 size/md5。
2. **原生语音 payload 是正确的。** 包含 `voice_item.media` 的 AES-128-ECB 密文的 `ITEM_VOICE` 请求通过 iLink CDN 顺利上传;`sendmessage` 返回 `ret=0`。
3. **个人微信客户端仍然无法渲染它。** 一项受控的 A/B 测试(标记文本 + 立即发送语音)显示文本已到达,而语音并未出现在接收者的客户端中。
4. **`ITEM_FILE` 音频附件确实可以到达。** 相同的媒体字节,相同的目标,不同的 `item.type` — 每次都能交付。
简化结论:在公共 iLink bot 路径上的 `ITEM_VOICE` 在 API 层被接受,但目前个人微信客户端无法渲染。音频文件附件路径是目前经过验证的可靠形态。
## 路线图 / 待验证假设
这些是值得测试的剩余假设:
1. 公共 iLink bot 出站 `ITEM_VOICE` 被 API 基础设施接受,但在个人客户端渲染之前被过滤。
2. 公共参考实现暴露了语音 payload schema,但不保证支持个人客户端交付。
3. 可能需要额外的私有/内部字段才能真正支持出站语音气泡。
4. 如果产品需求严格界定为“微信语音气泡”,目前最可信的剩余路径是微信客户端自动化:驱动官方客户端自行录制并发送音频。
## 配置
优先级顺序:
1. `.env` 中的 `HV_*` 值
2. `~/.hermes/weixin/accounts/*.json` 中的本地 Hermes Weixin 账户配置
3. 回退默认值
`.env` 示例:
```
HV_ILINK_BASE_URL=http://127.0.0.1:8080
HV_ILINK_TOKEN=
# TTS 引擎:"piper"(本地,离线)或 "edge"(Microsoft 云端,免费,无需 key)。
HV_TTS_ENGINE=piper
# Piper 配置(当 HV_TTS_ENGINE=piper)。由 `hermes-voice-setup` 设置。
HV_TTS_MODEL_PATH=
# edge-tts 配置(当 HV_TTS_ENGINE=edge)
HV_TTS_VOICE=zh-CN-XiaoxiaoNeural
HV_TTS_RATE=+0%
HV_TTS_PITCH=+0Hz
# STT(本地 faster-whisper,完全离线)
HV_STT_MODEL=base # tiny / base / small / medium / large-v3
HV_STT_COMPUTE_TYPE=int8 # int8 / int8_float16 / float16 / float32
HV_STT_DEVICE=cpu # cpu / cuda / auto
```
如果您已经使用 Hermes Weixin,iLink 字段将从现有账户配置中自动加载;通常您不需要手动填写它们。
## 开发
```
UV_CACHE_DIR=.uv-cache uv run pytest
```
`slow` 标记涵盖了 STT 往返测试(TTS → SILK → STT),该测试会在首次运行时下载 Whisper 模型。
## 范围
此代码库是:
- Hermes 微信 agent 的可用双向语音 I/O 层
- 可复现的 SILK / iLink 实验环境
- 基于证据的分析,说明公共 iLink bot 出站语音交付目前在个人微信客户端上的停止点
它**不是**一个即插即用的原生语音气泡发送器 — 该路径仍未解决,上文的文档解释了原因。
## 许可证
MIT — 请参阅 [LICENSE](LICENSE)。
标签:CNCF毕业项目, Python, 人工智能, 安全规则引擎, 微信机器人, 无后门, 用户模式Hook绕过, 语音合成, 语音识别, 逆向工具, 音频编解码