1. 项目缘起从“哑巴”机器人到能听会说的智能体最近在折腾我的OpenClaw智能助手功能是越来越强了能查天气、能控制智能家居甚至能帮我写点简单的代码。但总感觉少了点什么——它就像一个非常聪明的“哑巴”所有交互都得靠我在键盘上敲字。我想让它帮我定个闹钟得打开网页或者手机App输入指令我想让它播报一下今天的新闻它也只能把文字结果吐在屏幕上让我自己看。这体验离我心目中那个像“贾维斯”一样能无缝对话的智能管家还差得远。核心痛点就两个第一它没有“耳朵”听不懂我说话第二它没有“嘴巴”没法用语音回应我。我的目标很明确给这个基于大模型的OpenClaw装上语音交互能力让它能听会说。市面上实现语音交互的技术栈无外乎ASR自动语音识别和TTS文本转语音。方案选择上我首先排除了纯本地部署的模型。虽然像qwen3-asr-0.6b、nemotron 3.5 asr这类端侧模型最近很火在ollama上也能跑但实测下来在我不算差的开发机上识别速度和精度尤其是对中文口语的适应性还是没法让我满意。更别提TTS了想要一个自然流畅、接近真人的声音本地小模型比如一些开源的vits模型效果差强人意而好的大模型对算力要求又太高。所以我的目光转向了云服务。稳定、高质、开箱即用是云服务的核心优势。在对比了几家主流厂商后我最终选择了腾讯云语音。理由很直接首先它的语音识别和语音合成产品线非常成熟文档清晰提供了丰富的SDK和API其次作为国内云服务商对中文场景的优化更到位特别是口语化表达和常见噪音环境下的识别率最后它的免费额度对于个人开发者和小型项目非常友好足够前期验证和日常使用。我不需要从零开始训练一个asr或tts模型也不需要折腾docker部署复杂的语音服务直接调用API把计算压力交给云端我只需专注于OpenClaw本身的逻辑与云服务的集成。这个项目就是记录我如何将腾讯云的语音识别和语音合成能力无缝集成到OpenClaw中最终实现一个能用语音唤醒、语音指令控制、并用语音反馈的智能助手。整个过程涉及API申请、本地音频处理、与OpenClaw的WebSocket或HTTP接口对接、以及最终的联调测试。如果你也在为你的AI项目寻找一个可靠、高效的“耳朵”和“嘴巴”那么这篇实践记录或许能给你提供一个完整的参考路径。2. 战前准备腾讯云语音产品线梳理与资源创建在开始写代码之前我们必须先搞清楚腾讯云能提供什么武器以及如何获得它们。腾讯云语音相关的服务主要藏在“人工智能”产品大类下核心就是我们需要的两样语音识别ASR和语音合成TTS。2.1 核心产品选择通用场景下的最优解腾讯云的语音识别产品有好几个细分比如“通用语音识别”、“实时语音识别”、“录音文件识别”等。对于OpenClaw这种需要实时交互的场景“实时语音识别”是最佳选择。它支持长时间连续语音流式识别低延迟非常适合语音对话。而“语音合成”产品同样有多个声音可选我选择了“标准音色”中的“智云”音色男性听起来比较清晰自然当然你也可以选择“智瑜”女性或其他精品音色部分可能需要单独开通或付费。这里有一个关键点腾讯云的语音识别和语音合成是两个独立的服务拥有各自独立的API密钥、SDK和计费方式。这意味着你需要为它们分别开通权限、获取密钥。千万不要以为开通了一个就能通用。2.2 账号与资源创建步步为营第一步你需要有一个腾讯云账号。如果没有去官网注册即可这个过程比较常规可能需要实名认证。第二步进入 腾讯云控制台 在顶部搜索栏直接搜索“语音识别”和“语音合成”进入对应的产品页面。第三步开通服务。通常这两个服务都有免费额度比如语音识别每月有数小时的免费时长语音合成有免费字符数。直接点击开通即可不会立即产生费用足够我们开发测试。第四步也是最重要的一步获取访问密钥。这是你的代码和腾讯云服务之间的“通行证”。将鼠标悬停在控制台右上角的账号名称上点击“访问管理”。在左侧菜单进入“访问密钥” - “API密钥管理”。点击“新建密钥”你会得到一对SecretId和SecretKey。请立即妥善保存SecretKey因为它只显示这一次你可以将它保存在本地的密码管理器或环境变量中切勿提交到代码仓库。第五步虽然非必须但我强烈建议你创建一个“子账号”并授予最小权限而不是直接使用主账号的密钥。在“访问管理”-“用户”中创建子用户然后通过“策略”关联“QcloudASRFullAccess”语音识别全读写权限和“QcloudTTSFullAccess”语音合成全读写权限这类预设策略。然后使用这个子账号的密钥进行开发这样更安全。2.3 地域Region的选择调用API时需要指定服务地域。选择离你服务器或用户群体最近的地域能获得更低的网络延迟。例如如果你的服务器在国内可以选择ap-guangzhou广州。你可以在对应产品的API文档里找到所有可用的地域列表。至此我们的“弹药”API密钥和“地图”服务端点、地域都已就位。接下来我们就要开始构建连接本地OpenClaw和云端语音服务的桥梁了。3. 构建语音桥梁本地音频处理与腾讯云SDK集成有了云端的能力和通行证下一步就是在本地搭建一个中间层。这个中间层需要完成三件事1. 从麦克风采集音频2. 将音频流发送给腾讯云ASR并获取文本3. 将OpenClaw返回的文本发送给腾讯云TTS并播放音频。我们将使用Python作为粘合剂。3.1 环境搭建与SDK安装首先创建一个干净的Python虚拟环境是个好习惯。然后安装腾讯云官方Python SDK的核心包以及我们需要的音频处理库pip install tencentcloud-sdk-python pip install pyaudio # 用于音频采集和播放 pip install websocket-client # 如果需要与OpenClaw的WebSocket接口通信tencentcloud-sdk-python是一个大包包含了所有腾讯云服务的SDK。pyaudio是处理音频输入输出的利器但在某些系统上安装可能需要额外步骤比如在Linux上可能需要portaudio开发库。3.2 实现“耳朵”语音识别ASR客户端腾讯云实时语音识别API支持两种方式一句话识别适用于短语音和实时语音识别适用于长语音流。我们显然需要后者。这里的关键是使用“流式”接口。下面是一个高度简化的、展示核心流程的示例代码。在实际项目中你需要处理异常、管理连接生命周期、并将识别结果通过队列等方式传递给OpenClaw的主逻辑线程。from tencentcloud.common import credential from tencentcloud.common.profile.client_profile import ClientProfile from tencentcloud.common.profile.http_profile import HttpProfile from tencentcloud.asr.v20190614 import asr_client, models import pyaudio import threading import queue class ASRClient: def __init__(self, secret_id, secret_key, regionap-guangzhou): # 1. 初始化认证 cred credential.Credential(secret_id, secret_key) http_profile HttpProfile() http_profile.endpoint asr.tencentcloudapi.com client_profile ClientProfile() client_profile.httpProfile http_profile self.client asr_client.AsrClient(cred, region, client_profile) # 2. 初始化音频流参数 self.audio_format pyaudio.paInt16 self.channels 1 self.rate 16000 # 16kHz是ASR常用采样率 self.chunk 1600 # 每次读取的音频数据块大小 self.audio_queue queue.Queue() # 用于存放音频数据块 self.text_queue queue.Queue() # 用于存放识别出的文本 # 3. 创建实时识别请求并建立连接 self.req models.SentenceRecognitionRequest() # 这里实际上应该使用 CreateRecTask 或 实时流式接口 # 以下代码仅为流程示意真实流式接口调用更复杂 # 通常需要先调用 CreateRecTask 开启一个任务然后通过 DescribeTaskStatus 轮询或使用WebSocket接收结果 def start_listening(self): 开始从麦克风采集音频并送入队列 p pyaudio.PyAudio() stream p.open(formatself.audio_format, channelsself.channels, rateself.rate, inputTrue, frames_per_bufferself.chunk) print(开始监听...) while True: data stream.read(self.chunk, exception_on_overflowFalse) self.audio_queue.put(data) stream.stop_stream() stream.close() p.terminate() def process_audio(self): 从队列取音频调用腾讯云API进行识别 # 注意此处为示意真实流式调用需遵循腾讯云流式API文档 # 可能需要将音频数据编码为base64并分片发送 while True: audio_data self.audio_queue.get() # 构造请求参数这里以非流式的一句话识别为例 req_params { ProjectId: 0, SubServiceType: 2, EngSerViceType: 16k_zh, SourceType: 1, # 1表示语音数据为pcm格式 VoiceFormat: 1, # 1表示pcm UsrAudioKey: openclaw_session_001, Data: audio_data # 实际需要base64编码 } # 发送请求并获取结果此处需异常处理 # resp self.client.SentenceRecognition(req_params) # if resp.Result: # self.text_queue.put(resp.Result) print([ASR模拟] 收到音频块长度:, len(audio_data)) # 使用示例 if __name__ __main__: asr ASRClient(你的SecretId, 你的SecretKey) # 需要在新线程中运行音频采集和识别 listen_thread threading.Thread(targetasr.start_listening) process_thread threading.Thread(targetasr.process_audio) listen_thread.start() process_thread.start()注意上面的代码是概念演示。腾讯云实时语音识别的流式API调用逻辑比这复杂通常涉及建立WebSocket连接、按照特定协议如SpeechRecognition协议发送音频帧。你必须严格按照 官方流式识别API文档 来实现。核心步骤包括初始化请求、建立WebSocket连接、持续发送音频数据、并异步接收识别结果文本。3.3 实现“嘴巴”语音合成TTS客户端TTS的调用相对简单是标准的同步HTTP请求。我们将OpenClaw返回的文本发送过去接收二进制音频数据然后播放。from tencentcloud.common import credential from tencentcloud.common.profile.client_profile import ClientProfile from tencentcloud.common.profile.http_profile import HttpProfile from tencentcloud.tts.v20190823 import tts_client, models import pyaudio import io class TTSClient: def __init__(self, secret_id, secret_key, regionap-guangzhou): cred credential.Credential(secret_id, secret_key) http_profile HttpProfile() http_profile.endpoint tts.tencentcloudapi.com client_profile ClientProfile() client_profile.httpProfile http_profile self.client tts_client.TtsClient(cred, region, client_profile) self.audio_player pyaudio.PyAudio() def speak(self, text, voice_type101016): # 101016 对应“智云”音色 将文本合成为语音并播放 try: req models.TextToVoiceRequest() req.Text text req.SessionId openclaw_tts_session req.ModelType 1 # 默认模型 req.VoiceType voice_type req.Codec mp3 # 输出mp3格式 req.SampleRate 16000 # 可调节语速、音量等参数 # req.Speed 0 # req.Volume 0 resp self.client.TextToVoice(req) if resp.Audio is not None: # 将base64编码的音频数据解码并播放 import base64 audio_data base64.b64decode(resp.Audio) self._play_audio(audio_data) return True else: print(TTS合成失败无音频数据返回) return False except Exception as e: print(fTTS请求出错: {e}) return False def _play_audio(self, audio_bytes): 使用pyaudio播放音频字节流 stream self.audio_player.open(formatpyaudio.paInt16, channels1, rate16000, outputTrue) # 注意这里假设返回的是原始PCM如果是mp3需要先解码 # 简单起见假设返回的是PCM。实际腾讯云TTS返回base64编码的音频可能是mp3或pcm需根据Codec参数处理。 # 此处应加入音频解码逻辑如使用pydub库处理mp3 stream.write(audio_bytes) stream.stop_stream() stream.close() # 使用示例 if __name__ __main__: tts TTSClient(你的SecretId, 你的SecretKey) tts.speak(你好我是OpenClaw很高兴为你服务。)关键点_play_audio方法中我们假设音频数据是原始的PCM格式。但腾讯云TTS默认返回的是mp3或wav格式的base64字符串。你需要根据请求中Codec参数使用相应的库如pydub来解码音频数据然后再交给pyaudio播放。例如如果Codec设为mp3你需要先将base64解码再用pydub将mp3数据转换为PCM波形数据。4. 打通任督二脉OpenClaw与语音服务的对接策略现在“耳朵”ASR和“嘴巴”TTS的客户端我们已经有了雏形。接下来是最关键的一步如何让它们与OpenClaw本体协同工作OpenClaw通常通过HTTP API或WebSocket对外提供服务。我们的语音中间层需要扮演一个“智能网关”的角色。4.1 架构设计事件驱动与消息队列一个健壮的集成架构应该是异步、解耦的。我推荐使用事件驱动模型配合消息队列即使在单进程中也可以用queue.Queue实现来传递数据。这样音频采集、识别、OpenClaw处理、合成、播放这几个模块可以独立运行互不阻塞。基本工作流如下音频采集线程持续从麦克风读取PCM数据块放入“音频数据队列”。ASR处理线程从“音频数据队列”取出数据打包、调用腾讯云流式ASR API。将识别出的完整句子例如检测到静音或用户说完一句话后放入“文本指令队列”。OpenClaw通信线程从“文本指令队列”取出文本通过HTTP POST或WebSocket发送给OpenClaw的对话接口例如/v1/chat/completions。等待并获取OpenClaw返回的文本响应。TTS处理线程将OpenClaw返回的文本放入“TTS文本队列”。TTS客户端从队列取出文本调用腾讯云TTS API获得音频数据后放入“音频播放队列”。音频播放线程从“音频播放队列”取出音频数据调用播放器进行播放。4.2 与OpenClaw的通信实践假设你的OpenClaw服务运行在http://localhost:11434以Ollama为例并提供了兼容OpenAI API的接口。import requests import json import threading class OpenClawHandler: def __init__(self, base_urlhttp://localhost:11434): self.base_url base_url self.chat_endpoint f{base_url}/v1/chat/completions self.headers {Content-Type: application/json} def send_query(self, user_text, modelopenclaw:latest): 发送用户文本到OpenClaw并获取回复 payload { model: model, messages: [{role: user, content: user_text}], stream: False # 为简化先使用非流式响应 } try: response requests.post(self.chat_endpoint, headersself.headers, datajson.dumps(payload), timeout30) response.raise_for_status() result response.json() # 解析响应获取助手回复的文本 assistant_reply result[choices][0][message][content] return assistant_reply.strip() except requests.exceptions.RequestException as e: print(f请求OpenClaw失败: {e}) return 抱歉我好像出了点问题请稍后再试。 except (KeyError, IndexError, json.JSONDecodeError) as e: print(f解析OpenClaw响应失败: {e}) return 我无法理解刚才的回复。 # 在主逻辑中连接各个部分 def main_loop(asr_client, openclaw_handler, tts_client): text_queue queue.Queue() # ASR - OpenClaw tts_queue queue.Queue() # OpenClaw - TTS def openclaw_worker(): while True: user_text text_queue.get() print(f[用户指令] {user_text}) reply openclaw_handler.send_query(user_text) print(f[OpenClaw回复] {reply}) tts_queue.put(reply) def tts_worker(): while True: text_to_speak tts_queue.get() tts_client.speak(text_to_speak) # 启动工作线程 threading.Thread(targetopenclaw_worker, daemonTrue).start() threading.Thread(targettts_worker, daemonTrue).start() # 模拟ASR不断产生文本实际应从ASR线程的text_queue获取 # 这里用一个简单的输入循环代替 try: while True: user_input input(请输入指令模拟ASR输入: ) if user_input.lower() quit: break text_queue.put(user_input) except KeyboardInterrupt: print(\n程序退出)4.3 流式交互的优化上面的例子是“说完一整句-处理-回复”的模式。对于更自然的对话可以考虑流式响应。即OpenClaw一边生成文本TTS一边开始合成和播放虽然TTS仍需整句才能合成但可以按短语拆分。同时ASR也可以实现“中间结果”返回在用户说话时就能实时显示识别到的文字提升交互感。这需要更复杂的线程同步和状态管理但对体验提升巨大。5. 实战中的坑与填坑指南集成过程绝非一帆风顺我踩了好几个坑这里记录下来希望能帮你绕过去。5.1 音频格式与参数的精确匹配这是最常出问题的地方。腾讯云ASR对音频参数有严格要求。采样率SampleRate必须与你的音频采集设备设置、以及API请求参数一致。常用16k或8k。如果你用pyaudio以16k采样率录制但API请求里写了8k识别结果会是一团糟。位深度BitDepth通常为16位即pyaudio.paInt16。声道数Channels单声道1。编码格式实时识别通常支持PCM、WAV、SPEEX等。如果你发送的是PCM原始数据VoiceFormat参数要设为1并且数据不要包含WAV头。如果你先存成了WAV文件再发送要确保包含完整的头信息。踩坑实录我一开始图省事用sounddevice库录制的音频直接发送结果一直返回“音频数据解码失败”。后来发现sounddevice默认返回的是float32格式的数据而腾讯云ASR的PCM格式需要int16。解决方案是audio_data (audio_data * 32767).astype(np.int16)。5.2 网络延迟与超时处理语音交互对延迟非常敏感。你需要妥善处理网络抖动和超时。ASR连接保活流式识别连接需要维持。网络中断后需要有重连机制。腾讯云的流式WebSocket连接可能设有超时时间长时间无音频数据发送会导致连接关闭。OpenClaw响应超时如果OpenClaw处理某个复杂问题时间过长会导致整个对话卡住。务必为请求OpenClaw的HTTP调用设置合理的超时时间如30秒并做好超时异常处理给用户一个“思考超时”的语音反馈。异步与回调将所有网络请求ASR、OpenClaw、TTS都放在独立的线程或使用异步框架如asyncio中避免阻塞主线程或音频采集线程。5.3 资源管理与错误恢复API密钥管理切勿硬编码在代码中。使用环境变量或配置文件。我推荐用python-dotenv加载.env文件。音频设备释放pyaudio的PyAudio实例和stream对象一定要在程序退出或异常时正确关闭stop_stream(),close(),terminate()否则麦克风可能一直被占用导致其他程序无法使用。优雅退出设计一个信号处理机制如监听KeyboardInterrupt在程序退出时有序地停止所有线程、关闭所有连接和音频流。日志记录给每个模块加上详细的日志使用logging模块记录关键事件、发送的数据长度、接收的响应片段等。当出现“识别为空”、“合成失败”时日志是唯一的排查线索。5.4 提升交互体验的细节静音检测VAD不能一直把麦克风数据无脑送去做识别。需要加入静音检测算法只在检测到人声时才将音频数据送入ASR队列。这可以节省流量、减少无效识别并更准确地判断一句话的结束。可以使用webrtcvad这样的库。唤醒词实现一个本地的唤醒词检测例如用snowboy或Porcupine平时ASR处于低功耗监听状态只有听到“你好OpenClaw”这样的唤醒词后才开启连续识别这样更省电也更私密。TTS打断当TTS正在播报时如果用户突然说话应该能立即打断播报并开始新的识别。这需要TTS播放器支持中断并管理好状态机。通过以上这些步骤和注意事项你应该能够将一个“哑巴”OpenClaw成功升级为一个能听会说的智能语音助手。整个系统的复杂度不低但拆解成音频采集、ASR、逻辑处理、TTS、播放这几个模块后逐个实现和调试最终集成是一个非常有成就感的工程实践。最重要的是你获得了一个可扩展的语音交互框架未来可以轻松替换其他ASR/TTS服务或者增加新的技能模块。