1. 项目概述与核心价值

最近在折腾智能家居和语音交互,发现市面上的方案要么太“重”,需要一整套生态;要么太“傻”,识别不准还反应慢。直到我看到了GitHub上这个名为“SheikhAminul/ChatGPT-voice-control”的项目,眼前顿时一亮。这本质上是一个利用OpenAI的Whisper语音识别模型和ChatGPT大语言模型,将本地语音指令转化为智能响应的Python脚本。它最吸引我的地方在于其“轻量”和“智能”的结合——你不需要昂贵的智能音箱硬件,只需要一台能跑Python的电脑(甚至树莓派),就能获得一个理解力超强、能处理复杂指令的私人语音助手。

这个项目解决了几个核心痛点。首先,它实现了高质量的离线或在线语音转文本(STT)。Whisper模型在识别准确率,尤其是对中文的识别上,表现远超许多开源方案。其次,它接入了ChatGPT,这意味着你的语音指令不再局限于“开灯”、“关空调”这种预设的关键词匹配。你可以用更自然、更复杂的方式下达指令,比如“把客厅的灯光调到适合看电影的亮度,顺便把空调设为26度”,ChatGPT能够理解这句话的意图,并将其解析成具体的、可执行的动作命令。最后,整个流程是模块化的,你可以轻松地将ChatGPT生成的文本响应,通过自定义的逻辑,去控制家里的智能设备、执行系统命令,或者进行任何你想要的自动化操作。

简单来说,这个项目为你提供了一个强大的“大脑”(ChatGPT)和“耳朵”(Whisper),你需要做的,就是为它配上“手”和“脚”(即执行具体动作的代码)。无论是资深开发者想打造高度定制化的智能中控,还是编程爱好者想体验一把AI语音交互的魅力,这个项目都是一个极佳的起点。接下来,我将从环境搭建到核心代码解析,再到实战扩展,带你完整复现并深度定制这个项目。

2. 环境准备与依赖部署

工欲善其事,必先利其器。这个项目的运行依赖于一个稳定的Python环境以及几个关键的库。我强烈建议使用 conda venv 创建一个独立的虚拟环境,避免与系统其他Python项目产生依赖冲突。

2.1 创建与激活虚拟环境

如果你使用 conda (我个人更推荐,尤其在处理不同版本的Python和库时更省心),可以这样操作:

# 创建一个名为`chatgpt-voice`的Python3.9环境
conda create -n chatgpt-voice python=3.9
# 激活环境
conda activate chatgpt-voice

如果使用 venv

# 在项目目录下创建虚拟环境
python -m venv venv
# 激活环境 (Linux/macOS)
source venv/bin/activate
# 激活环境 (Windows)
venv\Scripts\activate

2.2 安装核心依赖

项目原作者可能提供了 requirements.txt ,但根据其核心功能,我们必须确保以下几个库的版本是兼容的。我根据实测,整理了一份更稳定的依赖列表:

pip install openai==0.28.1  # OpenAI API客户端
pip install whisper-openai  # 这是OpenAI官方维护的Whisper Python包,比`whisper`更推荐
pip install sounddevice  # 用于录制音频
pip install numpy  # 音频数据处理
pip install scipy  # 可能用于音频处理
pip install pydub  # 音频格式转换(如果需要处理mp3等)
pip install python-dotenv  # 管理环境变量,保护你的API Key

注意 whisper 模型本身在首次运行时会自动下载模型文件(如 base , small , medium )。 small 模型在精度和速度上取得了很好的平衡,适合大多数场景。如果你追求极致的准确率且机器性能足够(最好有GPU),可以考虑 medium 甚至 large 模型。

2.3 获取并配置OpenAI API密钥

这是项目的关键一步。你需要一个有效的OpenAI API密钥来调用ChatGPT和Whisper的API服务(如果你选择使用API而非本地Whisper模型)。

  1. 访问 OpenAI平台 并登录。
  2. 点击右上角个人头像,选择“View API keys”。
  3. 点击“Create new secret key”来生成一个新的密钥。 请立即复制并妥善保存这个密钥,关闭页面后将无法再次查看完整密钥。

为了安全地使用这个密钥,我们采用环境变量管理,而不是硬编码在脚本里。在项目根目录下创建一个名为 .env 的文件,内容如下:

OPENAI_API_KEY=你的实际API密钥

然后在你的Python脚本中,通过 python-dotenv 来加载:

from dotenv import load_dotenv
import os

load_dotenv()  # 加载.env文件中的环境变量
api_key = os.getenv("OPENAI_API_KEY")

2.4 音频输入设备检查

项目使用 sounddevice 库进行录音。在编写代码前,最好先确认你的麦克风能被正确识别。你可以运行一个简单的测试脚本:

import sounddevice as sd
print(sd.query_devices())  # 打印所有音频设备
# 通常,默认输入设备是我们要用的。记录下它的设备ID(通常是0)。

如果输出列表为空或找不到麦克风,你需要检查系统的音频设置,确保麦克风权限已授予给你的Python环境或终端。

3. 核心代码模块深度解析

原项目代码结构清晰,我们可以将其拆解为三个核心模块: 语音录制 语音转文本(STT) 文本交互与意图解析(ChatGPT) 。我将逐一解析,并补充关键细节和优化点。

3.1 语音录制模块:稳定获取音频流

录音的质量直接影响到Whisper识别的准确率。我们需要录制一段音频,并将其保存为Whisper支持的格式(如WAV)。这里的关键参数是采样率( samplerate )和录音时长。

import sounddevice as sd
import numpy as np
from scipy.io.wavfile import write
import tempfile
import os

def record_audio(duration=5, samplerate=16000, device=None):
    """
    录制指定时长的音频。
    Args:
        duration: 录音时长(秒)
        samplerate: 采样率,Whisper推荐16000 Hz
        device: 输入设备ID,None为默认设备
    Returns:
        audio_numpy: 录音的numpy数组
        temp_file_path: 临时WAV文件路径
    """
    print(f"开始录音,请说话...(最长{duration}秒)")
    # 录制音频,返回的是numpy数组
    audio_data = sd.rec(int(duration * samplerate),
                        samplerate=samplerate,
                        channels=1,  # 单声道足以满足语音识别
                        dtype='float32',
                        device=device)
    sd.wait()  # 等待录音完成
    print("录音结束。")

    # 将float32的音频数据转换为int16,这是WAV文件常用的格式
    audio_int16 = (audio_data * 32767).astype(np.int16)

    # 创建临时文件保存音频
    with tempfile.NamedTemporaryFile(suffix='.wav', delete=False) as tmpfile:
        temp_file_path = tmpfile.name
        write(temp_file_path, samplerate, audio_int16)

    return audio_data, temp_file_path

实操心得 duration 不宜设置过短,否则用户话没说完就结束了;也不宜过长,否则会一直录音占用资源。一个折中的方案是 实现一个“静音检测”(VAD)功能 ,当检测到用户停止说话一段时间后自动结束录音。这能极大提升体验。你可以使用 webrtcvad 这样的库来实现,虽然会增加一些复杂度,但对于追求产品化的项目来说是值得的。

3.2 语音转文本模块:Whisper的本地与云端抉择

这是项目的“耳朵”。你有两种选择:使用本地Whisper模型,或调用OpenAI的Whisper API。

方案一:本地Whisper模型(离线、免费,但消耗计算资源)

import whisper

def transcribe_local(audio_file_path, model_name="small"):
    """
    使用本地Whisper模型进行语音识别。
    Args:
        audio_file_path: 音频文件路径
        model_name: 模型大小,可选"tiny", "base", "small", "medium", "large"
    Returns:
        text: 识别出的文本
    """
    # 加载模型(首次运行会下载)
    model = whisper.load_model(model_name)
    # 执行识别
    result = model.transcribe(audio_file_path, language="zh")  # 指定中文,提升准确率
    return result["text"]

优势 :完全离线,无需网络,没有API调用费用,隐私性好。 劣势 :首次下载模型慢(小模型约500MB),转录速度取决于CPU/GPU性能,大模型对内存要求高。

方案二:OpenAI Whisper API(在线、准确率高、需付费)

from openai import OpenAI
import os
from dotenv import load_dotenv

load_dotenv()
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

def transcribe_api(audio_file_path):
    """
    调用OpenAI的Whisper API进行语音识别。
    Args:
        audio_file_path: 音频文件路径
    Returns:
        text: 识别出的文本
    """
    with open(audio_file_path, "rb") as audio_file:
        transcript = client.audio.transcriptions.create(
            model="whisper-1",
            file=audio_file,
            language="zh"  # 同样建议指定语言
        )
    return transcript.text

优势 :识别准确率通常是最高的一档,速度快,不消耗本地算力。 劣势 :需要网络,按使用量付费(目前价格是$0.006 /分钟),音频数据会上传至OpenAI。

注意事项 :对于个人或低频使用,API方案的成本极低(识别100分钟才0.6美元),且体验最好。对于高频使用或对隐私、网络有严格要求的场景,本地模型是必选项。我建议在开发调试阶段使用API,部署时根据实际情况选择。

3.3 智能交互与意图解析模块:赋予ChatGPT“思考”能力

这是项目的“大脑”。我们将用户语音识别出的文本(即指令)发送给ChatGPT,并引导它将其解析为结构化的、可执行的命令。这里的关键在于 系统提示词(System Prompt) 的设计。

from openai import OpenAI
import os
import json
from dotenv import load_dotenv

load_dotenv()
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

def ask_chatgpt(user_query):
    """
    向ChatGPT发送用户查询,并期望返回一个可解析的JSON指令。
    Args:
        user_query: 用户的语音指令文本
    Returns:
        dict: 解析后的指令字典,如果解析失败返回None
    """
    # 精心设计的系统提示词,决定了ChatGPT的输出格式和行为
    system_prompt = """
    你是一个智能家居控制助手。用户会向你发出自然语言指令。
    你的任务是将指令解析为以下JSON格式:
    {
        "action": "动作名称,如:turn_on, turn_off, adjust, query, custom",
        "target": "操作目标,如:living_room_light, air_conditioner, all_lights",
        "parameters": {
            // 动作参数,例如:
            // "brightness": 80, // 亮度百分比
            // "temperature": 26, // 温度值
            // "color": "warm_white" // 颜色
        },
        "response": "给用户的自然语言回复,确认操作或告知结果"
    }

    规则:
    1. 如果指令不明确或无法识别,`action`设为`unknown`,`target`和`parameters`可为空,`response`中友好地请求用户澄清。
    2. 对于查询类指令(如“客厅温度多少?”),`action`设为`query`。
    3. 对于复杂指令(如“先开灯再开空调”),可以返回一个包含多个指令对象的列表(用JSON数组表示)。
    4. `response`字段必须用中文回复。
    """

    try:
        response = client.chat.completions.create(
            model="gpt-3.5-turbo-1106",  # 或 "gpt-4",支持JSON模式
            messages=[
                {"role": "system", "content": system_prompt},
                {"role": "user", "content": user_query}
            ],
            temperature=0.1,  # 低温度值使输出更确定,减少随机性
            response_format={"type": "json_object"}  # 强制返回JSON,这是gpt-3.5-turbo-1106及以后模型的新特性
        )
        # 解析返回的JSON
        result_text = response.choices[0].message.content
        instruction = json.loads(result_text)
        return instruction

    except json.JSONDecodeError as e:
        print(f"ChatGPT返回的不是有效JSON: {result_text}")
        return None
    except Exception as e:
        print(f"调用ChatGPT API出错: {e}")
        return None

系统提示词设计解析

  • 角色定义 :明确告诉AI“你是一个智能家居控制助手”,限定其回答范围。
  • 输出格式 :强制要求返回一个特定的JSON结构。这是实现 结构化输出 的关键,使得后续代码可以稳定地解析 action , target , parameters 等字段。
  • 规则细化 :处理边界情况,如未知指令、查询指令、复合指令。这能大大提高系统的鲁棒性。
  • 语言指定 :要求 response 用中文,保证给用户的语音反馈是母语。

模型选择与参数

  • gpt-3.5-turbo-1106 是性价比之选,响应快,成本低,并且支持 response_format 参数来强制JSON输出,这比让模型自己“尽量输出JSON”要可靠得多。
  • temperature=0.1 使得输出非常稳定,对于控制指令这种需要确定性的场景非常合适。

4. 主循环与执行器整合

现在我们将三个模块串联起来,形成一个完整的语音控制循环,并添加一个简单的“执行器”来模拟设备控制。

import time
import os

def execute_instruction(instruction):
    """
    根据解析后的指令执行具体操作(这里是模拟)。
    在实际应用中,这里会调用具体的硬件API(如Home Assistant, MQTT, 或厂商SDK)。
    Args:
        instruction: 从ChatGPT返回的指令字典
    """
    if not instruction:
        print("无效指令,跳过执行。")
        return

    # 处理可能是列表的指令(复合指令)
    instructions_list = instruction if isinstance(instruction, list) else [instruction]

    for instr in instructions_list:
        action = instr.get("action")
        target = instr.get("target")
        params = instr.get("parameters", {})
        response = instr.get("response", "操作完成")

        print(f"[执行] 动作: {action}, 目标: {target}, 参数: {params}")
        print(f"[助手回复] {response}")

        # 这里是实际控制逻辑的占位符
        if action == "turn_on":
            # 调用打开目标设备的函数,例如:homeassistant.turn_on(target)
            print(f"模拟:打开 {target}")
        elif action == "turn_off":
            print(f"模拟:关闭 {target}")
        elif action == "adjust":
            if "brightness" in params:
                print(f"模拟:将 {target} 亮度调整为 {params['brightness']}%")
            if "temperature" in params:
                print(f"模拟:将 {target} 温度设置为 {params['temperature']}°C")
        elif action == "query":
            print(f"模拟:查询 {target} 的状态...")
            # 这里可以真实查询设备状态并更新response
        elif action == "unknown":
            print(f"指令不明确: {response}")
        else:
            print(f"未定义的动作: {action}")
        time.sleep(0.5)  # 模拟操作间隔

def main_loop():
    """主循环:录音->识别->解析->执行"""
    print("语音控制助手已启动。按下 Ctrl+C 退出。")
    try:
        while True:
            input("按回车键开始录音...")  # 简单的触发方式,可替换为热词检测

            # 1. 录音
            audio_data, audio_path = record_audio(duration=7)  # 录音7秒

            # 2. 语音转文本
            print("正在识别语音...")
            # 选择一种转录方式
            # user_text = transcribe_local(audio_path, model_name="small")  # 本地
            user_text = transcribe_api(audio_path)  # API
            print(f"识别结果: {user_text}")

            # 3. 发送给ChatGPT解析
            print("正在解析指令...")
            instruction = ask_chatgpt(user_text)

            # 4. 执行指令
            if instruction:
                execute_instruction(instruction)
            else:
                print("指令解析失败,请重试。")

            # 清理临时音频文件
            os.unlink(audio_path)
            print("-" * 40)

    except KeyboardInterrupt:
        print("\n程序退出。")

if __name__ == "__main__":
    main_loop()

这个 main_loop 实现了一个基本的交互流程。它使用“按回车键”作为触发方式,在实际应用中,你可以将其替换为:

  1. 全局快捷键触发 :使用 pynput 等库监听某个快捷键。
  2. 物理按钮触发 :配合树莓派GPIO,连接一个实体按钮。
  3. 语音热词唤醒 :集成 Porcupine Snowboy 等离线热词检测库,实现真正的“嘿,Siri”式唤醒。

5. 实战扩展:连接真实智能家居平台

上面的 execute_instruction 函数只是打印模拟信息。要让项目真正发挥作用,必须将其与真实的智能家居平台或设备连接。这里以目前最流行的开源家庭自动化平台 Home Assistant 为例,展示如何实现真实控制。

5.1 Home Assistant 集成准备

假设你的Home Assistant已经安装并运行在本地网络(例如 http://192.168.1.100:8123 ),并且你已经创建了一个长期访问令牌(Long-Lived Access Token)。

  1. 在Home Assistant中,点击你的用户名 -> 滚动到底部 -> “创建令牌”。为你的语音助手创建一个令牌并复制保存。

  2. 在Python项目中安装Home Assistant的官方REST客户端:

    pip install homeassistant-api
    

    或者使用更通用的 requests 库。

5.2 实现真实的执行器

我们修改 execute_instruction 函数,使其能通过Home Assistant的API控制实体。

import requests
import os
from dotenv import load_dotenv

load_dotenv()

# 从环境变量读取Home Assistant配置
HA_BASE_URL = os.getenv("HA_BASE_URL", "http://192.168.1.100:8123")
HA_ACCESS_TOKEN = os.getenv("HA_ACCESS_TOKEN")  # 你的长期访问令牌

HEADERS = {
    "Authorization": f"Bearer {HA_ACCESS_TOKEN}",
    "Content-Type": "application/json",
}

def execute_instruction_ha(instruction):
    """
    通过Home Assistant API执行指令。
    """
    if not instruction or not HA_ACCESS_TOKEN:
        print("HA配置缺失或指令无效。")
        return

    instructions_list = instruction if isinstance(instruction, list) else [instruction]

    for instr in instructions_list:
        action = instr.get("action")
        target = instr.get("target")  # 这里target应该是HA中的entity_id,如 `light.living_room`
        params = instr.get("parameters", {})
        response = instr.get("response", "操作完成")

        print(f"[HA执行] 动作: {action}, 目标: {target}")

        # 将ChatGPT的通用动作映射到HA的服务调用
        service_data = {"entity_id": target}
        service_data.update(params)  # 合并额外参数

        try:
            if action in ["turn_on", "turn_off"]:
                # 调用 homeassistant.turn_on/turn_off 服务
                service = f"homeassistant.{action}"
                ha_response = requests.post(
                    f"{HA_BASE_URL}/api/services/homeassistant/{action}",
                    headers=HEADERS,
                    json={"entity_id": target}
                )
            elif action == "adjust":
                # 调整操作需要调用具体域的服务,例如 light.turn_on 并带参数
                # 我们需要根据target的域来判断,例如 light.living_room -> 域是 light
                domain = target.split('.')[0] if '.' in target else 'homeassistant'
                if domain == 'light':
                    # 调用 light.turn_on 服务来调整亮度、颜色等
                    service_data = {"entity_id": target}
                    if "brightness" in params:
                        # HA亮度范围是0-255
                        service_data["brightness"] = int(params["brightness"] * 255 / 100)
                    if "color" in params:
                        # 这里需要将颜色名称映射为RGB或色温值,简化处理
                        pass
                    ha_response = requests.post(
                        f"{HA_BASE_URL}/api/services/light/turn_on",
                        headers=HEADERS,
                        json=service_data
                    )
                elif domain == 'climate':
                    # 控制空调温度
                    if "temperature" in params:
                        service_data = {"entity_id": target, "temperature": params["temperature"]}
                        ha_response = requests.post(
                            f"{HA_BASE_URL}/api/services/climate/set_temperature",
                            headers=HEADERS,
                            json=service_data
                        )
            elif action == "query":
                # 查询实体状态
                ha_response = requests.get(
                    f"{HA_BASE_URL}/api/states/{target}",
                    headers=HEADERS
                )
                if ha_response.status_code == 200:
                    state_info = ha_response.json()
                    print(f"[状态查询] {target} 的状态是: {state_info.get('state')}")
                    # 可以更新instr['response'],用于后续语音反馈
                    instr['response'] = f"{target} 当前状态是 {state_info.get('state')}。"

            # 检查HA API响应
            if 'ha_response' in locals() and ha_response.status_code not in [200, 201]:
                print(f"HA API调用失败: {ha_response.status_code}, {ha_response.text}")
                instr['response'] = "操作似乎没有成功,请检查设备。"
            else:
                print(f"[HA执行成功]")

        except requests.exceptions.ConnectionError:
            print("无法连接到Home Assistant,请检查网络和地址。")
            instr['response'] = "无法连接到智能家居系统。"
        except Exception as e:
            print(f"执行过程中发生未知错误: {e}")
            instr['response'] = "操作执行出错。"

        # 播放或显示助手回复
        print(f"[助手回复] {instr.get('response')}")
        # 这里可以接入一个TTS引擎,将回复文本转为语音播放出来

关键点解析

  1. 实体ID映射 :这是最核心的一步。你需要将ChatGPT解析出的通用 target (如 living_room_light )映射到Home Assistant中具体的 entity_id (如 light.yeelight_living_room )。一个更智能的做法是,在系统提示词中让ChatGPT直接输出 entity_id ,但这需要你预先给ChatGPT“灌输”一份你的设备清单。更实用的方法是在本地维护一个映射字典。
  2. 服务调用 :Home Assistant通过“服务”来控制设备。不同设备域( light , climate , switch 等)有各自的服务和参数。上述代码提供了基本框架,实际应用中需要根据你的设备类型进行扩展。
  3. 错误处理 :网络错误、实体不存在、服务调用失败等情况都必须考虑,并给出友好的用户反馈(通过 response 字段)。

5.3 添加文本转语音(TTS)反馈

一个完整的语音交互系统,除了“听”,还应该能“说”。我们可以利用操作系统的TTS引擎或第三方API来实现。

使用pyttsx3(离线,跨平台)

pip install pyttsx3
import pyttsx3

def speak_text(text):
    """使用系统T引擎朗读文本"""
    try:
        engine = pyttsx3.init()
        engine.say(text)
        engine.runAndWait()
    except Exception as e:
        print(f"TTS出错: {e}")

# 在execute_instruction_ha函数最后,调用 speak_text(instr.get('response'))

使用Edge-TTS(在线,声音自然)

pip install edge-tts
import asyncio
import edge_tts

async def speak_text_edge(text, voice="zh-CN-XiaoxiaoNeural"):
    """使用Edge TTS朗读文本(需要网络)"""
    communicate = edge_tts.Communicate(text, voice)
    await communicate.save("temp_feedback.mp3")
    # 使用playsound或pydub播放temp_feedback.mp3
    os.system("afplay temp_feedback.mp3")  # macOS
    # os.system("start temp_feedback.mp3")  # Windows
    os.unlink("temp_feedback.mp3")

# 注意:调用异步函数需要在异步环境中,或使用 asyncio.run(speak_text_edge(...))

将TTS集成后,你的助手就能在每次执行指令后,用语音回复“已打开客厅灯”或“当前室内温度是25度”,体验瞬间提升一个档次。

6. 性能优化与常见问题排查

在实际部署中,你可能会遇到延迟、识别不准、资源占用高等问题。这里分享一些优化和排查经验。

6.1 降低端到端延迟

延迟是影响体验的首要因素。整个流程的延迟主要来自:录音时长、Whisper识别时间、ChatGPT API响应时间、网络延迟。

优化策略

  1. 实现流式语音识别(VAD + 流式Whisper) :不要等用户说完固定的7秒再识别。使用 webrtcvad 检测到语音开始和结束,一旦检测到说话间隙(例如静音超过500毫秒),立即将已录制的音频片段发送给Whisper进行 流式识别 。OpenAI的Whisper API支持 response_format=verbose_json 并设置 timestamp ,但真正的流式需要更复杂的处理。一个折中方案是使用更短的固定录音间隔(如2秒)并连续识别。
  2. 缓存与预热 :如果是本地Whisper模型,首次加载很慢。可以在程序启动时预先加载模型( whisper.load_model )。对于ChatGPT,可以考虑对常见指令(如“开灯”、“关灯”)的解析结果进行缓存,避免重复调用API。
  3. 模型选型 :在准确率和速度间权衡。Whisper模型选 tiny base 速度最快;ChatGPT模型选 gpt-3.5-turbo-instruct (补全模型)可能比聊天模型响应稍快,但指令解析能力可能稍弱。
  4. 并行处理 :当录音还在进行最后一段时,就可以开始将前面已录好的部分发送给Whisper,实现流水线操作。

6.2 提升语音识别准确率

识别不准会导致后续所有环节出错。

排查与改进

  1. 检查音频质量 :确保麦克风正常工作,没有过多环境噪音。可以尝试增加录音时的 gain (增益),或使用 noise 库进行简单的降噪预处理。
  2. 明确语言上下文 :在调用Whisper时,始终指定 language=”zh” 。对于中英混合场景,可以尝试不指定语言或使用 language=”zh” 并设置 task=”transcribe”
  3. 使用更准确的模型 :如果本地 small 模型不准,果断换用 medium 或调用Whisper API。API的准确率在绝大多数环境下都是最好的。
  4. 提示词工程 :在将文本发送给ChatGPT前,可以对识别结果进行简单的后处理。例如,如果识别结果包含明显的无意义音节或重复词,可以用规则进行过滤。更高级的做法是,将识别文本和可能的候选列表(如果Whisper提供了的话)一起送给ChatGPT,让它来选择最合理的那个。

6.3 处理复杂指令与上下文

最初的系统提示词只能处理单轮指令。如何实现多轮对话和上下文记忆?

实现思路

  1. 维护对话历史 :在 ask_chatgpt 函数中,不再只发送当前用户指令,而是发送一个包含历史对话的 messages 列表。
    conversation_history = []  # 全局或会话级变量
    
    def ask_chatgpt_with_history(user_query):
        global conversation_history
        # 将历史记录和最新查询组合
        messages = [
            {"role": "system", "content": system_prompt},
            *conversation_history[-6:],  # 只保留最近3轮对话(6条消息),防止token超限
            {"role": "user", "content": user_query}
        ]
        response = client.chat.completions.create(...)
        assistant_reply = response.choices[0].message.content
        # 更新历史
        conversation_history.append({"role": "user", "content": user_query})
        conversation_history.append({"role": "assistant", "content": assistant_reply})
        return json.loads(assistant_reply)
    
  2. 在提示词中明确上下文 :在 system_prompt 中加入关于上下文的说明,例如“请参考之前的对话历史来理解指代,比如‘把它关掉’中的‘它’可能指代上一句提到的设备。”

6.4 资源占用与部署建议

  • CPU/GPU占用 :本地运行Whisper small 模型,推理时单核CPU占用可能接近100%。如果部署在树莓派4B上,可能会比较卡顿,建议使用 tiny base 模型,或者使用API方案。
  • 内存占用 :加载Whisper模型会占用数百MB内存。确保你的部署设备有足够的内存。
  • 长期运行 :将主脚本作为系统服务(如使用 systemd )运行,并配置看门狗,确保程序崩溃后能自动重启。
  • 隐私与安全 :如果使用API,你的音频和指令文本会经过OpenAI的服务器。如果涉及非常私密的对话或指令,请务必使用本地模型方案,并确保网络传输安全(HTTPS)。

7. 项目总结与未来展望

通过一步步拆解和实现,我们不仅复现了“SheikhAminul/ChatGPT-voice-control”项目的核心,还对其进行了深度的扩展和优化。这个项目的精髓在于其 架构的优雅 :前端(语音采集与识别)与后端(意图解析与执行)通过一个结构化的JSON协议解耦。这意味着,你可以随时替换其中的任何一个模块。

例如,你可以把Whisper换成其他STT引擎(如阿里云、腾讯云的语音识别服务);可以把ChatGPT换成 Claude、Gemini 或其他本地大语言模型(如通过Ollama部署的Llama 3);可以把Home Assistant换成米家、Apple HomeKit或直接控制MQTT设备。这种模块化设计赋予了项目极强的生命力和适应性。

从我个人的实战经验来看,将这个系统部署到一台旧笔记本或树莓派上,让它7x24小时运行,作为家庭智能中枢的“语音交互层”,是一个非常酷且实用的选择。它弥补了现有商业语音助手在 复杂指令理解 本地化深度控制 上的不足。你可以用它实现这样的场景:“把我书房的电脑、显示器、台灯和空调都打开,并把空调调到除湿模式”——只需一句话,ChatGPT会帮你拆解成多个有序的指令,并逐一执行。

当然,目前这还是一个需要主动触发(按回车或说热词)的“半自动”方案。未来的优化方向可以朝着 全双工连续对话 更强的本地化 迈进,比如集成本地LLM完全离线运行,但这需要更强的硬件支持。无论如何,这个项目已经为我们打开了一扇门,展示了利用当今最先进的AI模型构建个性化、智能化工具的无限可能。剩下的,就交给你的想象力和动手能力去发挥了。

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐