车万女仆AI对话全本地部署方法

: 本文仅仅只是提供一个方向方法和一个实际成功的案例,并不能解决所有问题。笔者是个外行,时间精力都有限,如有问题,欢迎一起交流探讨。

1、对话模型本地部署

如何使用ollama部署本地模型在网上已经有很多相关教程了。具体的部署过程这里不再赘述,但仍要强调:

  • 选择模型时注意模型大小。注意要给TTS还有MC光影留够足够的显存,为了将对话本地化而放弃游戏舒适性是并不可取的。一个参考是:笔者使用4060Ti-16G,此时如果MC开启DistantHorizons+IterationT光影,7b大小的模型是极限了。
  • 选择模型时注意模型的类型和能力。许多推理模型的输出并不能与现在的TLM完美兼容,比如deepseek-r1系列,qwq等。同样不同的模型对话灵活度,以及输出语言都是不同的。如果你使用gamma3:4b,那么其并不能完成额外给语音合成返回日语的需求;但如果联网使用deepseek-V3,则是可以的。

使用ollama部署完本地模型之后,只要在TLM的设置里更改即可。(可以在MC的TLM模组设置里点击“点击此处打开站点文件进行编辑”,也可以找到MC版本文件夹/config-touhou_little_maid/available_sites.yml 进行编辑。)具体设置如下:

Ollama:
  type: chat
  url: http://127.0.0.1:11434/v1
  api_key: ollama
  models:
  - gemma3:1b
  - gemma3:4b

最后的 gemma3:1b 两行换成你下载的模型。在游戏中 /tlm ai_chat reload 之后就可以看见配置了。

  • 不得不提的是:语言模型和tts不同,如果想要一个比较好的效果,是比较吃算力的。如果你不在意购买tokens和隐私性的问题,笔者仍然建议使用在线模型。视屏中笔者使用的是siliconflow部署的deepseek-V3 。

2、TTS本地部署

这是相对麻烦的一点。由于TLM本身的兼容是面向fish audio的,我们需要自己改代码对TLM进行兼容。这里的演示使用的是GPT-SoVITS。

关于GPT-SoVITS的部署同样不再赘述。这里强烈建议windows用户选择一键包进行安装,不要去捣鼓python环境了。解压完成之后双击go-webui.bat,等待一会儿后如果出现了网页端,就是部署成功。

现在我们需要修改 api.py 。我们复制 api.pyapi-tlm.py 作为备份,并在 api-tlm.py 中进行修改。在github上我们可以找到TLM的源代码,对比二者之后可以发现,TLM请求的text字段和GPT-SoVITS的text字段是相同的。那么对于仅仅是做出语音响应而言,我们手动补齐 GPT-SoVITS 的剩余字段就可以使用了。(如果你需要实现更多的功能,比如不同的女仆使用不同声音模型等,可以同样通过更改api-tlm.py实现,这里不进行过于深入的探讨。)以下是需要修改的地方:

  • 在 GPT-SoVITS 根目录下的 config.py 中写入你的音频模型。位置在第 6 至 7 行。

    sovits_path = "SoVITS_weights_v2/小鸟游六花.pth"
    gpt_path = "GPT_weights_v2/小鸟游六花.ckpt"
    

    一个完整的GPT模型会包含 .pth 文件, .ckpt 文件以及参考音频。请将 .pth 文件放在 GPT-SoVITS根目录/SoVITS_weights_v2 下,将 .ckpt 文件放在 GPT_SoVITS根目录/GPT_weights_v2 下。

  • 在 GPT-SoVITS 根目录下的 api-tlm.py 中修改两段api接口,原本代码为(在约900行的位置 ):

    @app.post("/")
    @app.get("/")
    

    TLM访问的端口是 /tts ,这里把两个 / 都改为 /tts

    @app.post("/tts")
    @app.get("/tts")
    
  • 继续更改 api-tlm.py ,在上一步更改的两行之后,补足tts需要的设置(主要是要指定语言:"zh"表示中文, "ja"表示日文):

            json_post_raw.get("text_language", "ja"),
            json_post_raw.get("cut_punc"),
            json_post_raw.get("top_k", 15),
            json_post_raw.get("top_p", 0.5),
            json_post_raw.get("temperature", 0.7),
            json_post_raw.get("speed", 1.0),
            json_post_raw.get("inp_refs", [])
            
            
            text_language: str = "ja",
            cut_punc: str = None,
            top_k: int = 15,
            top_p: float = 0.5,
            temperature: float = 0.7,
            speed: float = 1.0,
            inp_refs: list = Query(default=[])
    
  • 继续更改 api-tlm.py ,返回 opus 文件格式而非 wav (更改handle函数,692行)。原本715行处为

    return StreamingResponse(get_tts_wav(refer_wav_path, prompt_text, prompt_language, text, text_language, top_k, top_p, temperature, speed, inp_refs), media_type="audio/"+media_type)
    

    # 注释掉这一行(或者直接删除),改为以下内容:

        #保存为WAV文件
        WAVE_STREAM = get_tts_wav(refer_wav_path, prompt_text, prompt_language, text, text_language, top_k, top_p, temperature, speed, inp_refs)
        RAW_DATA = b""
        for chunk in WAVE_STREAM:
            RAW_DATA += chunk
        with wave.open("temp.wav", 'wb') as OUTPUT_WAV:
            OUTPUT_WAV.setnchannels(1)
            OUTPUT_WAV.setsampwidth(2)
            OUTPUT_WAV.setframerate(speaker_list["default"].sovits.hps.data.sampling_rate)
            if RAW_DATA.startswith(b'RIFF'):
                with open("temp.wav", 'wb') as f:
                    f.write(RAW_DATA)
            else:
                OUTPUT_WAV.writeframes(RAW_DATA)    
        #用命令行 ffmpeg 转换为 opus 文件
        __COMMAND = [
            "ffmpeg",
            "-i", "temp.wav",    # 输入文件
            "-c:a", "libopus",   # 指定音频编码器
            "-y",                # 覆盖输出文件
            "temp.opus"
        ]
    
        # 执行转换命令
        __COMMAND_RESULT = subprocess.run(
            __COMMAND,
            check=True,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True
        )
        # 流式读取opus文件
        OPUS_file = "temp.opus"
        def file_streamer():
            with open(OPUS_file, "rb") as f:
                while chunk := f.read(4096):  # 4KB 分块读取
                    yield chunk
        # 流式响应,替代原本的return行
        return StreamingResponse(
            content=file_streamer(),
            media_type="audio/opus",
            headers={
                "Content-Disposition": f"inline; filename={OPUS_file}",
                "X-File-Size": str(os.path.getsize(OPUS_file))
            }
    

    至此,对 api-tlm.py 的修改结束。

  • 现在要启用GPT-SoVITS api服务。我们在GPT-SoVITS根目录下使用命令行(cmd)运行 api-tlm.py 并且传入相关参数:

    runtime\python.exe api-tlm.py -dr "refer_wave/小鸟游六花/幸せすぎて死亡フラグが立ってるんじゃないかと思うくらい.wav" -dt "幸せすぎて死亡フラグが立ってるんじゃないかと思うくらい" -dl "ja"
    

    这里,-dr 表示参考音频, -dt 代表参考音频内容, -dl 代表参考音频语言。注意参考音频需要和你选择的模型相匹配,否则音频效果会比较差。同时注意参考音频的路径,这里的例子是在 GPT-SoVITS根目录/refer_wave/小鸟游六花/幸せすぎて死亡フラグが立ってるんじゃないかと思うくらい.wav 。当然也可以是绝对路径,比如 D:/AI/GPT-SoVITS/refer_wave/小鸟游六花/幸せすぎて死亡フラグが立ってるんじゃないかと思うくらい.wav

    运行这一行命令后,等待十几秒,如果你看到 Uvicorn running on http://0.0.0.0:9880 (Press CTRL+C to quit) 即为成功。此时打开浏览器,访问 http://127.0.0.1:9880/docs 能够看到 FastAPI 界面。我们可以在网页的蓝色 /tts 条目下试用。由于我们更改了函数返回,此时网页端不能播放音频文件。我们可以在 GPT-SoVITS 根目录下的 temp.wav 或者 temp.opus 查看音频输出。

  • 最后,我们更改TLM的设置文件即可:

    GPT-SoVITS:
      type: tts
      url: http://127.0.0.1:9880/
      api_key: my_api_key
      models:
      - 小鸟游六花
    

    这里只要填对 typeurl 就行。 api_key 这一行不是空的、不是 null 就行; models 有就行。此时在游戏内重载 /tlm ai_chat reload 之后,就可以在女仆设置里看到相应选项了。

    至此礼成。

api-tlm.py 代码: https://gitee.com/chy-2003/share/blob/master/api-tlm.py

posted @ 2025-03-17 14:54  chy_2003  阅读(3657)  评论(1)    收藏  举报