
在开发智能应用时语音交互和自然语言理解是两大核心能力。然而将文本转语音TTS、语音转文本STT和大语言模型LLM三者无缝集成常常面临成本高昂、接口复杂、网络依赖强等难题。本文旨在分享一套完全免费、可本地部署的“三合一”整合方案涵盖从环境搭建、核心代码实现到生产优化的全流程。无论你是想为个人项目添加语音助手功能还是为企业级应用探索低成本AI方案都能从本文获得可直接复用的代码和清晰的配置思路。1. 背景与核心概念为什么需要 TTS、STT 与 LLM 的整合在构建具备自然交互能力的AI应用时单一的文本或语音接口往往无法满足复杂场景的需求。一个完整的智能对话闭环通常包含“听、想、说”三个环节这正是STT、LLM和TTS三种技术协同工作的结果。STT语音转文本负责将用户输入的音频信号转换为计算机可处理的文本。它是人机交互的“耳朵”其准确度直接决定了后续流程的输入质量。常见的应用场景包括语音助手、会议转录、语音搜索等。LLM大语言模型作为系统的“大脑”它负责理解STT转换后的文本进行意图识别、上下文分析、知识问答、内容生成等复杂的自然语言处理任务。LLM的能力决定了交互的智能程度。TTS文本转语音将LLM生成的文本回复转换为自然流畅的语音输出。它是人机交互的“嘴巴”其自然度和情感表现力极大地影响用户体验常用于智能客服、有声内容播报、导航提示等。将三者整合的优势在于成本可控利用开源或免费的API避免对昂贵商业服务的依赖。隐私安全支持本地化部署敏感音频和对话数据无需上传至第三方。高度定制可根据具体场景自由选择和微调每一环节的模型或引擎。流程闭环构建从语音输入到语音输出的端到端自动化流程提升产品完整性。接下来我们将从环境准备开始一步步实现这个“三合一”系统。2. 环境准备与版本说明本方案设计为跨平台核心逻辑使用Python实现因其在AI和语音处理领域有丰富的生态库支持。我们将选用当前以常见稳定版本为例广泛使用的开源工具和框架。基础运行环境操作系统Ubuntu 20.04/22.04 LTS, Windows 10/11, macOS Monterey 及以上本文以Ubuntu为例命令略有差异。Python版本 3.8 - 3.10。推荐使用 3.8 或 3.9 以保证库的最佳兼容性。包管理工具pip(20.0)虚拟环境强烈建议使用venv或conda创建独立环境避免包冲突。核心组件选型与版本为了达成“免费”和“可本地化”的目标我们进行如下选型STT引擎Vosk。这是一个离线的开源语音识别工具包支持多种语言识别准确度高且对硬件要求相对友好。我们将使用其Python接口。TTS引擎Edge-TTS命令行工具 pyttsx3备选。Edge-TTS通过命令行调用微软Edge浏览器的在线TTS服务声音质量高、自然度好且目前免费。虽然需要网络但API稳定是快速获得高质量语音的优选。pyttsx3一个纯离线的TTS库跨平台支持系统自带的语音引擎如Windows的SAPI5Linux的espeak。作为离线备选方案。LLM接口OllamaLangChain。Ollama一个强大的工具可以让你在本地轻松运行、管理和与各种开源大语言模型如Llama 2, Mistral, Gemma等交互。LangChain一个用于开发由LLM驱动的应用程序的框架。我们将用它来标准化与Ollama的交互并方便未来扩展。项目结构预览在开始前我们先规划一下项目目录这有助于理解代码组织。tts_stt_llm_demo/ ├── main.py # 主程序入口 ├── config.yaml # 配置文件可选 ├── requirements.txt # Python依赖列表 ├── models/ # 存放Vosk离线模型 │ └── vosk-model-small-en-us-0.15/ # 英文小模型 ├── audio_cache/ # 缓存生成的TTS音频文件 └── README.md3. 核心组件安装与配置3.1 创建Python虚拟环境与安装基础依赖首先创建并激活一个干净的Python环境。# 创建项目目录并进入 mkdir tts_stt_llm_demo cd tts_stt_llm_demo # 创建虚拟环境以venv为例 python3 -m venv venv # 激活虚拟环境 # Linux/macOS source venv/bin/activate # Windows # venv\Scripts\activate # 升级pip pip install --upgrade pip创建requirements.txt文件并写入以下依赖# requirements.txt vosk0.3.45 sounddevice0.4.6 # 用于录音 numpy1.24.3 # 音频处理常用库 langchain0.1.0 langchain-community0.0.10 pyttsx32.90 requests2.31.0 # 用于可能的HTTP请求 PyYAML6.0 # 用于读取配置然后安装它们pip install -r requirements.txt注意Edge-TTS我们稍后通过命令行安装因为它主要是一个命令行工具。3.2 配置 Vosk (STT) 离线模型Vosk需要下载对应的语音识别模型。我们以轻量级的英文小模型为例。访问 Vosk模型下载页面 。找到vosk-model-small-en-us-0.15(约40MB) 并下载压缩包。将压缩包解压到项目根目录的models文件夹下。最终路径应为./models/vosk-model-small-en-us-0.15。你也可以使用其他更大更精确的模型如中文模型vosk-model-small-cn-0.22。3.3 安装与验证 Ollama (LLM)Ollama的安装非常简单。# Linux/macOS 一键安装 curl -fsSL https://ollama.com/install.sh | sh # Windows: 直接从官网 https://ollama.com 下载安装程序并运行。安装完成后启动Ollama服务通常安装后会自动运行。然后拉取一个开源模型例如轻量级的llama2:7b或更高效的mistral:7b。# 拉取模型首次运行需要下载时间较长 ollama pull mistral:7b # 验证模型是否运行进行简单对话 ollama run mistral:7b在出现的提示符后输入Hello, how are you?如果模型能回复说明LLM环境就绪。按CtrlD退出交互模式。3.4 安装 Edge-TTSEdge-TTS是一个Python包但它主要通过命令行调用。# 在之前激活的虚拟环境中安装 pip install edge-tts安装后可以在终端测试# 生成一个语音文件 edge-tts --text Hello, this is a test. --write-media hello_test.mp3如果生成了hello_test.mp3文件并能播放说明TTS组件工作正常。4. 核心代码实现构建“三合一”管道我们将按照录音(STT) - 理解(LLM) - 回复(TTS)的流程编写核心代码。4.1 实现语音识别 (STT with Vosk)创建stt_module.py文件实现录音和识别功能。# stt_module.py import json import queue import sys import sounddevice as sd from vosk import Model, KaldiRecognizer class SpeechToText: def __init__(self, model_path./models/vosk-model-small-en-us-0.15): 初始化Vosk识别器。 :param model_path: Vosk离线模型的路径 try: self.model Model(model_path) except Exception as e: print(f无法加载Vosk模型请检查路径: {model_path}) print(f错误信息: {e}) sys.exit(1) self.recognizer KaldiRecognizer(self.model, 16000) self.sample_rate 16000 self.audio_queue queue.Queue() def audio_callback(self, indata, frames, time, status): 声音输入回调函数将音频数据放入队列。 if status: print(f音频输入状态: {status}, filesys.stderr) self.audio_queue.put(bytes(indata)) def listen_and_transcribe(self, duration5): 录制一段音频并将其转换为文本。 :param duration: 录音时长秒 :return: 识别出的文本字符串失败返回None print(f\n 请开始说话录音 {duration} 秒...) transcription None # 开始录音流 with sd.RawInputStream(samplerateself.sample_rate, blocksize8000, dtypeint16, channels1, callbackself.audio_callback): sd.sleep(duration * 1000) # 录音时长 print(录音结束处理中...) # 处理队列中的音频数据 while not self.audio_queue.empty(): data self.audio_queue.get() if self.recognizer.AcceptWaveform(data): result json.loads(self.recognizer.Result()) transcription result.get(text, ) else: # 获取部分结果实时反馈用 partial_result json.loads(self.recognizer.PartialResult()) # print(f实时识别: {partial_result.get(partial, )}) # 可选打印实时识别 if transcription and len(transcription.strip()) 0: print(f✅ 识别结果: {transcription}) return transcription.strip() else: print(❌ 未识别到有效语音。) return None if __name__ __main__: # 模块测试代码 stt_engine SpeechToText() text stt_engine.listen_and_transcribe(duration3) if text: print(f最终文本: {text})4.2 实现大语言模型交互 (LLM with Ollama LangChain)创建llm_module.py文件使用 LangChain 标准化调用 Ollama。# llm_module.py from langchain_community.llms import Ollama from langchain.prompts import PromptTemplate from langchain.chains import LLMChain class LanguageModelProcessor: def __init__(self, model_namemistral:7b): 初始化LangChain的Ollama LLM链。 :param model_name: Ollama中已拉取的模型名称 # 初始化Ollama LLM指定基础URL默认本地11434端口 self.llm Ollama(modelmodel_name, base_urlhttp://localhost:11434) # 定义一个提示模板让模型扮演一个友好的助手 prompt_template 你是一个有帮助的AI助手。请用简洁、友好的方式回答用户的问题。 如果问题不清楚可以请求澄清。 用户问题{user_input} 助手回答 self.prompt PromptTemplate( input_variables[user_input], templateprompt_template ) # 创建链 self.chain LLMChain(llmself.llm, promptself.prompt) def generate_response(self, user_input): 根据用户输入生成回复。 :param user_input: 用户输入的文本 :return: 模型生成的回复文本 if not user_input or len(user_input.strip()) 0: return 我没有听到您说什么。 print(f 思考中... (输入: {user_input})) try: response self.chain.run(user_inputuser_input) print(f 生成回复: {response}) return response.strip() except Exception as e: print(fLLM调用出错: {e}) return 抱歉我现在有点困惑请稍后再试。 if __name__ __main__: # 模块测试代码 llm_processor LanguageModelProcessor() test_input What is the capital of France? answer llm_processor.generate_response(test_input) print(f测试回复: {answer})4.3 实现文本转语音 (TTS with Edge-TTS)创建tts_module.py文件封装 Edge-TTS 的命令行调用。# tts_module.py import subprocess import os import pyttsx3 from pathlib import Path class TextToSpeech: def __init__(self, use_edge_ttsTrue, voiceen-US-JennyNeural, cache_dir./audio_cache): 初始化TTS引擎。 :param use_edge_tts: 是否使用Edge-TTSTrue否则使用pyttsx3离线引擎False :param voice: Edge-TTS的语音名称如 en-US-JennyNeural, zh-CN-XiaoxiaoNeural :param cache_dir: 音频缓存目录 self.use_edge_tts use_edge_tts self.voice voice self.cache_dir Path(cache_dir) self.cache_dir.mkdir(exist_okTrue) # 确保缓存目录存在 if not use_edge_tts: # 初始化离线引擎 self.offline_engine pyttsx3.init() # 设置语速、音量等属性可选 self.offline_engine.setProperty(rate, 150) self.offline_engine.setProperty(volume, 0.9) def speak(self, text, save_to_fileNone): 将文本转换为语音并播放/保存。 :param text: 要合成的文本 :param save_to_file: 如果提供则保存为文件如 output.mp3否则直接播放 :return: 生成的音频文件路径如果保存了的话 if not text: print(⚠️ 文本为空无法合成语音。) return None print(f 正在合成语音: {text[:50]}...) if self.use_edge_tts: # 使用Edge-TTS return self._speak_with_edge_tts(text, save_to_file) else: # 使用pyttsx3离线引擎 return self._speak_with_pyttsx3(text, save_to_file) def _speak_with_edge_tts(self, text, save_to_file): 使用Edge-TTS合成语音。 import edge_tts import asyncio async def _async_speak(): communicate edge_tts.Communicate(text, self.voice) if save_to_file: output_path self.cache_dir / save_to_file if not os.path.isabs(save_to_file) else Path(save_to_file) await communicate.save(str(output_path)) print(f✅ 语音已保存至: {output_path}) return str(output_path) else: # 直接播放需要系统有播放mp3的能力 player edge_tts.AudioPlayer() await communicate.stream() async for chunk in communicate.stream(): if chunk[type] audio: player.add(chunk[data]) await player.play() print(✅ 语音播放完毕。) return None try: return asyncio.run(_async_speak()) except Exception as e: print(fEdge-TTS合成失败: {e}) # 降级到离线引擎 print(尝试使用离线引擎...) self.use_edge_tts False return self._speak_with_pyttsx3(text, save_to_file) def _speak_with_pyttsx3(self, text, save_to_file): 使用pyttsx3离线合成语音。 try: if save_to_file: output_path self.cache_dir / save_to_file if not os.path.isabs(save_to_file) else Path(save_to_file) self.offline_engine.save_to_file(text, str(output_path)) self.offline_engine.runAndWait() print(f✅ 离线语音已保存至: {output_path}) return str(output_path) else: self.offline_engine.say(text) self.offline_engine.runAndWait() print(✅ 离线语音播放完毕。) return None except Exception as e: print(f离线TTS合成失败: {e}) return None if __name__ __main__: # 模块测试代码 - 使用Edge-TTS tts_online TextToSpeech(use_edge_ttsTrue, voiceen-US-JennyNeural) tts_online.speak(Hello, this is a test of the text to speech system., save_to_filetest_online.mp3) # 模块测试代码 - 使用离线引擎 tts_offline TextToSpeech(use_edge_ttsFalse) tts_offline.speak(This is an offline test., save_to_filetest_offline.wav)4.4 集成主程序最后创建main.py文件将三个模块串联起来形成一个完整的交互循环。# main.py import time from stt_module import SpeechToText from llm_module import LanguageModelProcessor from tts_module import TextToSpeech def main_loop(): 主交互循环 print( * 50) print( 免费 TTS STT LLM 三合一系统启动) print( * 50) # 1. 初始化各个模块 print([1/3] 初始化语音识别模块 (STT)...) stt SpeechToText(model_path./models/vosk-model-small-en-us-0.15) print([2/3] 初始化语言模型模块 (LLM)...) # 请确保Ollama服务正在运行且模型名正确 llm LanguageModelProcessor(model_namemistral:7b) # 或 llama2:7b print([3/3] 初始化语音合成模块 (TTS)...) # 使用Edge-TTS在线引擎语音选择美式英语Jenny tts TextToSpeech(use_edge_ttsTrue, voiceen-US-JennyNeural) print(\n✅ 所有模块初始化完成) print( 提示系统将循环进行‘听你说 - 思考 - 回答’。) print( 输入 quit, exit 或按下 CtrlC 退出程序。\n) # 2. 开始交互循环 while True: try: # 步骤A: 语音输入与识别 user_text stt.listen_and_transcribe(duration5) # 录音5秒 if not user_text: continue # 没识别到内容重新开始循环 # 简单的退出命令判断识别出的文本 if user_text.lower() in [quit, exit, stop, 退出]: print(收到退出指令再见) tts.speak(Goodbye!) break # 步骤B: 大语言模型处理 bot_response llm.generate_response(user_text) # 步骤C: 语音输出 # 为了演示我们将回复保存为文件并播放 timestamp int(time.time()) audio_filename fresponse_{timestamp}.mp3 tts.speak(bot_response, save_to_fileaudio_filename) print(- * 30) # 分隔每次对话 except KeyboardInterrupt: print(\n\n程序被用户中断。) break except Exception as e: print(f\n⚠️ 主循环发生未知错误: {e}) print(尝试继续运行...) time.sleep(1) # 避免错误循环导致CPU占用过高 if __name__ __main__: main_loop()5. 运行与验证确保所有步骤已完成并且Ollama服务在后台运行。启动Ollama服务如果未运行ollama serve # 或者直接运行模型也会启动服务 # ollama run mistral:7b在项目根目录下运行主程序python main.py预期交互流程程序启动初始化三个模块。提示“请开始说话录音5秒...”此时对着麦克风清晰地说一句英文例如 “What is the weather like today?”录音结束后Vosk会识别文本并打印。LangChain调用本地的Mistral模型进行思考生成回复文本并打印。Edge-TTS将回复文本合成语音保存为audio_cache/response_xxxxxx.mp3并自动播放。一次对话结束进入下一次录音等待。至此一个完整的、免费的、可本地运行的 TTSSTTLLM 三合一系统就成功运行起来了。6. 常见问题与排查思路在实际部署和运行中你可能会遇到以下问题问题现象可能原因排查步骤与解决方案运行python main.py报ModuleNotFoundError依赖未安装或虚拟环境未激活。1. 确认终端路径在项目根目录。2. 执行pip list检查vosk,langchain,edge-tts等包是否存在。3. 若缺失重新运行pip install -r requirements.txt。Vosk 识别不出任何语音1. 麦克风未正确连接或未被授权。2. 音频采样率不匹配。3. 模型语言与输入语音不匹配。1. 检查系统音频设置确保麦克风是默认输入设备。2. 尝试使用sounddevice查询设备python -c import sounddevice; print(sounddevice.query_devices())。3. 确认下载的Vosk模型语言如英文与你说话的语种一致。4. 尝试在安静环境下清晰、缓慢地说话。Ollama 连接错误 (Connection refused)Ollama 服务未启动。1. 新开一个终端运行ollama serve并保持运行。2. 在主终端中运行curl http://localhost:11434/api/tags测试API是否可达。3. 确认llm_module.py中的base_url与 Ollama 服务地址一致。Edge-TTS 报错或没有声音1. 网络问题无法连接微软服务。2. 指定的voice不存在或不可用。3. 系统缺少MP3播放器。1. 检查网络连接。2. 运行edge-tts --list-voices查看所有可用语音更换一个语音名称如zh-CN-XiaoxiaoNeural。3. 对于播放问题可以优先使用save_to_file功能保存文件然后用本地播放器打开确认。程序运行缓慢LLM响应慢本地LLM推理需要消耗大量CPU/GPU资源。1. 确认电脑配置尤其是内存是否足够7B模型通常需要8GB以上内存。2. 考虑使用更小的模型如tinyllama。3. 在ollama run时添加--num-gpu参数利用GPU加速如有NVIDIA显卡。4. 这只是演示流程生产环境可考虑优化或使用API。录音时间固定为5秒不灵活代码中listen_and_transcribe(duration5)写死了时长。修改stt_module.py中的逻辑例如改为检测静音端点VSR或通过按键控制录音开始/结束。这需要更复杂的音频处理逻辑。7. 最佳实践与工程建议将本方案用于实际项目时需要考虑以下方面以提升稳定性、性能和用户体验。7.1 配置化管理将模型路径、语音名称、Ollama地址、录音参数等硬编码信息提取到配置文件如config.yaml或.env文件中。# config.yaml stt: model_path: ./models/vosk-model-small-cn-0.22 # 切换为中文模型 sample_rate: 16000 llm: model_name: mistral:7b base_url: http://localhost:11434 timeout: 30 tts: use_edge_tts: true voice: zh-CN-XiaoxiaoNeural # 中文语音 offline_engine_rate: 150 app: record_duration: 7 exit_keywords: [退出, quit, exit]在主程序中用PyYAML库读取配置。7.2 异步处理与性能优化当前的流程是同步的录音 - 识别 - LLM生成 - TTS合成。其中LLM生成可能耗时数秒到数十秒会阻塞整个程序。优化建议引入异步编程asyncio或消息队列。将LLM调用放入独立线程或进程主线程在等待时可以提供“正在思考”的语音或视觉反馈。流式响应对于LLM可以探索Ollama的流式输出API实现逐词生成和TTS减少用户等待的“空白期”。7.3 错误处理与降级策略网络或服务不稳定是常态。TTS降级如tts_module.py所示当Edge-TTS失败时自动切换到本地的pyttsx3。LLM降级可以配置一个备用的LLM API如开源的OpenAI兼容API当本地Ollama不可用时切换。STT降级Vosk离线识别是本地保障。也可以集成一个在线的STT服务如SpeechRecognition库调用Google Web Speech API作为备选但需注意隐私和网络。7.4 音频处理增强噪音抑制在录音回调中引入简单的噪音抑制算法或使用如noisereduce库提升Vosk在嘈杂环境下的识别率。静音检测实现基于能量的静音检测VAD让用户自由控制说话时长而不是固定录音。音频格式转换确保传递给Vosk的音频格式采样率、位深、声道完全符合模型要求。7.5 安全与隐私本地化优先本方案的核心优势是数据不出本地。确保Vosk模型、Ollama模型均从官方渠道下载并部署在可信环境中。敏感信息过滤在将STT识别出的文本发送给LLM前可增加一个过滤层移除或脱敏可能的个人身份信息PII。对话历史管理如果LLM需要上下文记忆避免在内存中长期存储明文对话记录。可考虑短期缓存或用户授权后的加密存储。7.6 扩展方向集成RAG使用LangChain的RetrievalQA链将本地文档库接入LLM让助手能够回答特定领域知识。多模态结合视觉模型升级为“看、听、想、说”的多模态系统。Web服务化使用FastAPI或Flask将核心功能封装成HTTP API供Web或移动前端调用。唤醒词集成像Porcupine这样的离线唤醒词引擎实现“Hey Assistant”式的触发。这套整合方案提供了一个坚实的起点。它验证了利用免费开源工具构建完整语音AI交互流程的可行性。你可以根据项目需求对每个模块进行深度定制和替换例如使用更精确的Whisper模型做STT更换为ChatGLM等中文LLM或接入更专业的TTS服务。关键在于理解数据流音频-文本-智能文本-音频和模块间的接口设计剩下的便是针对具体场景的优化和打磨了。