给 Claude Code 的 AI 助手配上语音:从零到会说话的完整教程

作者:Nicc29jl
日期:2026-05-12
效果:每次 AI 回复后自动生成声音并播放


一、整体架构

Claude Code 回复 → Stop Hook → tts.sh → cc_voice_auto.py → 星瞳TTS模型 → WAV文件 → 自动播放

核心思路:利用 Claude Code 的 Stop Hook 机制,在每次 AI 回复完成后自动触发脚本,将回复文本转为语音并播放。


二、环境准备

2.1 硬件要求

  • NVIDIA 显卡(本教程使用 RTX 4060 Laptop 8GB)
  • 至少 6GB 显存(推理最低需求)

2.2 软件安装

1. 安装 Miniconda / Anaconda

创建 Python 3.9 环境:

conda create -n sovits python=3.9
conda activate sovits

2. 安装 FFmpeg

winget install FFmpeg

安装后确保 FFmpeg 在系统 PATH 中。

3. 安装 PyTorch(CUDA 版本)

pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126

2.3 获取 GPT-SoVITS

从 gitcode 克隆 GPT-SoVITS V4 仓库:

git clone https://gitcode.com/GPT-SoVITS/GPT-SoVITS.git
cd GPT-SoVITS

2.4 下载预训练模型

需要下载以下模型文件:

  • GPT-SoVITS V4 预训练模型gsv-v4-pretrained(含 s2Gv4.pthvocoder.pth
  • BERT 模型chinese-roberta-wwm-ext-large
  • 中文 HuBERTchinese-hubert-base

将模型放置到对应目录:

GPT-SoVITS/
  GPT_SoVITS/
    pretrained_models/
      gsv-v4-pretrained/
      chinese-roberta-wwm-ext-large/
      chinese-hubert-base/

2.5 Windows 兼容性修复

GPT-SoVITS 在 Windows 上需要以下代码修改:

1. jieba_fast → jieba

文件:GPT_SoVITS/text/chinese.pychinese2.pytone_sandhi.py

import jieba_fast 改为 import jieba,将 jieba_fast.xxx 改为 jieba.xxx
(jieba_fast 需要 C++ 编译,Windows 上容易失败)

2. opencc 改为可选

文件:GPT_SoVITS/text/g2pw/onnx_api.py

将繁简转换改为 try/except 可选导入。

3. x_transformers 降级

Python 3.9 不支持新版 x_transformers 语法:

pip install x_transformers==1.42.0

4. 单卡训练修复(如需训练)

文件:GPT_SoVITS/s2_train.pys2_train_v3.py

Windows 不支持 GLOO 分布式训练,单卡时需要跳过 dist.init_process_group 和 DDP 包装。


三、获取语音模型

3.1 方案一:使用现成模型(推荐)

本教程使用**星瞳(XingTong)**预训练模型,中文女声效果优秀。

将模型文件放置到:

D:\GPT-SoVITS\GPT_SoVITS\pretrained_models\XingTong\XingTong\
  ├── gpt.ckpt
  ├── sovits.pth
  └── ref.wav

3.2 方案二:自己训练

如果你想要定制音色,需要准备:

  1. 目标说话人的音频(建议 30 分钟以上清晰干声)
  2. 使用 WebUI 进行数据预处理(切割 → ASR 识别 → BERT/HuBERT 特征提取 → 语义 Token)
  3. 修改训练代码支持 Windows 单卡训练
  4. 训练 GPT 模型和 SoVITS 模型

四、TTS 脚本

4.1 语音生成脚本 cc_voice_auto.py

"""AI语音自动生成 - 输入文字,输出语音并播放"""
import sys, os, soundfile as sf, subprocess, re, time

def strip_emoji(text):
    """过滤 emoji 等非 TTS 友好字符"""
    emoji_pat = re.compile("["
        u"\U0001F300-\U0001FAFF"
        u"\U0001FB00-\U0001FFFF"
        u"\U00002700-\U000027BF"
        u"\U00002600-\U000026FF"
        u"\U0001F000-\U0001F02F"
        u"\U0001F0A0-\U0001F0FF"
        u"\U0000FE00-\U0000FE0F"
        u"\U0000200D"
        "]+", flags=re.UNICODE)
    return emoji_pat.sub('', text).strip()

def clean_for_tts(text):
    """清洗文本:只保留中文、数字、中文标点(星瞳模型不擅长英文)"""
    text = re.sub(r'`[^`]*`', '', text)
    text = re.sub(r'\*\*[^*]*\*\*', '', text)
    text = re.sub(r'\*[^*]*\*', '', text)
    text = re.sub(r'https?://\S+', '', text)
    # 保留:中文、CJK符号、全角字符、数字、空格、中文标点
    text = re.sub(r"[^一-鿿 -〿＀-￯0-9 \n,。!?;:、“”‘’()…—~~]+", '', text)
    text = re.sub(r'\n+', ',', text)
    text = re.sub(r'\s+', ' ', text)
    return text.strip()

BASE = r"D:\GPT-SoVITS"
GPT = BASE + r"\GPT_SoVITS\pretrained_models\XingTong\XingTong\gpt.ckpt"
SOVITS = BASE + r"\GPT_SoVITS\pretrained_models\XingTong\XingTong\sovits.pth"
REF = BASE + r"\GPT_SoVITS\pretrained_models\XingTong\XingTong\ref.wav"
OUT_DIR = r"C:\Users\你的用户名\Desktop\CC语音"

os.chdir(BASE)
sys.path.insert(0, "GPT_SoVITS"); sys.path.insert(0, ".")
os.environ["is_half"] = "True"
os.environ["gpt_path"] = GPT
os.environ["sovits_path"] = SOVITS
os.environ["bert_path"] = BASE + r"\GPT_SoVITS\pretrained_models\chinese-roberta-wwm-ext-large"
os.environ["cnhubert_base_path"] = BASE + r"\GPT_SoVITS\pretrained_models\chinese-hubert-base"

from GPT_SoVITS.inference_webui import change_gpt_weights, change_sovits_weights, get_tts_wav

change_gpt_weights(gpt_path=GPT)
change_sovits_weights(sovits_path=SOVITS)

def speak(text):
    text = strip_emoji(text.strip()).replace("CC", "西西").replace("cc", "西西")
    text = clean_for_tts(text)
    if not text or len(text) < 2:
        return None
    try:
        result = list(get_tts_wav(
            ref_wav_path=REF, prompt_text="", prompt_language="中文",
            text=text, text_language="中文",
            top_k=20, top_p=0.6, temperature=0.6, sample_steps=16, speed=1))
        if result:
            sr, audio = result[-1]
            ts = time.strftime("%H%M%S")
            filename = f"CC_{ts}.wav"
            filepath = os.path.join(OUT_DIR, filename)
            sf.write(filepath, audio, sr)
            return filepath
    except Exception as e:
        print(f"[语音错误] {e}", file=sys.stderr)
    return None

if __name__ == "__main__":
    if len(sys.argv) >= 3 and sys.argv[1] == "--file":
        with open(sys.argv[2], "r", encoding="utf-8") as f:
            text = f.read().strip()
    elif len(sys.argv) > 1:
        text = sys.argv[1]
    else:
        text = sys.stdin.read().strip()

    if text:
        filepath = speak(text)
        if filepath:
            print(filepath)
            # 自动播放
            subprocess.Popen(['powershell', '-c',
                f'(New-Object System.Media.SoundPlayer "{filepath}").PlaySync();'],
                stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        else:
            print("FAILED", file=sys.stderr)

4.2 Hook 脚本 tts.sh

保存到 C:\Users\你的用户名\.claude\hooks\tts.sh

#!/usr/bin/env bash
# Claude Code Stop Hook - AI 回复后自动生成语音
LOGF="$HOME/.claude/hooks/tts_debug.log"
STDIN_FILE="C:/Users/你的用户名/.claude/hooks/.tts_stdin.json"
MSG_FILE="C:/Users/你的用户名/.claude/hooks/.tts_msg.txt"

# 保存 Claude Code 传来的 JSON 数据
cat > "$STDIN_FILE"

# 从 JSON 中提取 AI 回复文本,写入 UTF-8 文件(避免 CLI 编码问题)
python路径/python -c "
import json
d = json.load(open('C:/Users/你的用户名/.claude/hooks/.tts_stdin.json', 'r', encoding='utf-8'))
msg = d.get('last_assistant_message', '')
if msg:
    with open('C:/Users/你的用户名/.claude/hooks/.tts_msg.txt', 'w', encoding='utf-8') as f:
        f.write(msg)
    print('OK')
else:
    print('EMPTY')
" >>"$LOGF" 2>>"$LOGF"

# 生成语音
if [ -s "$MSG_FILE" ]; then
    echo "$(date): generating..." >> "$LOGF"
    python路径/python C:/Users/你的用户名/cc_voice_auto.py --file "$MSG_FILE" 2>>"$LOGF"
else
    echo "$(date): no message" >> "$LOGF"
fi

4.3 配置 Claude Code Hook

编辑 C:\Users\你的用户名\.claude\settings.json

{
  "permissions": {
    "allow": [
      "Bash(你的python路径/python C:/Users/你的用户名/cc_voice_auto.py *)",
      "Bash(find */CC语音/* -name *.wav -mmin *)"
    ]
  },
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "bash ~/.claude/hooks/tts.sh",
            "timeout": 120
          }
        ]
      }
    ]
  }
}

注意"matcher": "" 不可省略,否则 hook 格式校验不通过。


五、踩坑记录与解决方案

以下是实际配置过程中遇到的 5 个关键问题:

坑 1:stdin 管道在命令替换中不稳定

症状:Hook 收到数据但 Python 读不到 stdin

原因$(python -c "sys.stdin.read()")$() 命令替换可能不继承管道 stdin

解决:先用 cat > file 把 stdin 保存到临时文件,再让 Python 读取文件

坑 2:2>/dev/null 吞掉所有错误

症状:Hook 静默失败,无从排查

解决:将所有 2>/dev/null 改为 2>>debug.log,建立调试日志体系

坑 3:Emoji 导致 GBK 编码崩溃

症状'gbk' codec can't encode character '\U0001f3a4'

原因:Windows 中文系统 CLI 使用 GBK 编码,emoji(如 🎤)无法编码

解决:TTS 前用正则过滤所有 emoji 和特殊 Unicode 符号

坑 4:中英混合文本让模型产出垃圾音频(最隐蔽)

症状:生成 WAV 文件但只听见几个字,其余全是杂音——没有任何异常抛出

原因:星瞳是纯中文 TTS 模型,英文单词/技术术语(API、JSON、stdin 等)让它生成无意义波形但不报错

解决:添加 clean_for_tts() 函数,TTS 前将英文、特殊符号全部过滤,只保留中文+数字+中文标点。

教训:不要依赖"先试原文、失败再清洗"的两步策略——模型对中英混合不抛异常但产出垃圾,无法触发重试。直接全量清洗。

坑 5:CLI 参数编码损耗(根因)

症状:中文文本经 bash 传给 Windows Python 后变成乱码,clean_for_tts() 返回空字符串

原因:MSYS2/Git Bash 传参给 Windows 原生程序时,UTF-8 文本经过系统 GBK 代码页转换,部分字符丢失

解决:改用 --file 参数,将文本写入 UTF-8 文件,Python 直接从文件读取。彻底绕开 CLI 编码转换。


六、调试技巧

6.1 手动测试 Hook

echo '{"last_assistant_message":"你好这是一条测试"}' | bash ~/.claude/hooks/tts.sh

6.2 直接测试 TTS 引擎

cd D:/GPT-SoVITS
python -c "
import sys; sys.path.insert(0, 'GPT_SoVITS')
# ... 完整推理代码见上文
"

6.3 检查产物

  • 桌面 CC语音/ 文件夹下的 WAV 文件
  • ~/.claude/hooks/tts_debug.log 调试日志

6.4 常见失败原因速查

症状 可能原因 检查方法
无任何语音文件 Hook 未触发 检查 settings.json 格式
有日志但显示 MSG empty JSON 解析失败 检查 .tts_stdin.json 内容
日志显示 FAILED TTS 生成失败 检查 tts_text_debug.txt 确认文本是否正常
有 WAV 但音质差/少字 中英混合文本 确认 clean_for_tts() 已过滤英文

七、资源链接

  • GPT-SoVITS 仓库:https://gitcode.com/GPT-SoVITS/GPT-SoVITS
  • 预训练模型合集 HuggingFace:https://huggingface.co/zhaijifu67/so-vits-4.0-collect
  • B站 GPT-SoVITS-V4 教程:搜索 “GPT-SoVITS V4 推理”

后记

从"AI 只能打字"到"AI 能跟我说话",中间的每一步都是踩着 Bug 走过来的。希望这篇教程能帮你少走弯路,让你的 AI 助手也快点开口说话!

如果你配好了自己的 AI 语音,欢迎分享你的音色选择和效果~

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐